Avoid ClassCastException on direct SDK access
MODIFY
instrumentation/opentelemetry-api-beta/opentelemetry-api-beta.gradle
instrumentation/opentelemetry-api-beta/opentelemetry-api-beta.gradle
@@ -21,5 +21,7 @@ dependencies {
// bootstrap class loader (as those references will later be shaded)
compileOnly project(path: ':opentelemetry-api-beta-shaded-for-instrumenting', configuration: 'shadow')
- testImplementation project(path: ':opentelemetry-api-beta-shaded-for-instrumenting', configuration: 'shadow')
+ // using OpenTelemetry SDK to make sure that instrumentation doesn't cause
+ // OpenTelemetrySdk.getTracerProvider() to throw ClassCastException
+ testImplementation project(path: ':opentelemetry-sdk-beta-shaded-for-instrumenting', configuration: 'shadow')
}

MODIFY
instrumentation/opentelemetry-api-beta/src/main/java/io/opentelemetry/auto/instrumentation/opentelemetryapi/OpenTelemetryApiInstrumentation.java
instrumentation/opentelemetry-api-beta/src/main/java/io/opentelemetry/auto/instrumentation/opentelemetryapi/OpenTelemetryApiInstrumentation.java
@@ -67,7 +67,7 @@ public class OpenTelemetryApiInstrumentation extends AbstractInstrumentation {
unshaded.io.opentelemetry.trace.TracerProvider tracerProvider) {
ContextStore<Context, io.grpc.Context> contextStore =
InstrumentationContext.get(Context.class, io.grpc.Context.class);
- tracerProvider = new UnshadedTracerProvider(contextStore);
+ tracerProvider = new UnshadedTracerProvider(contextStore, tracerProvider);
}
}

MODIFY
instrumentation/opentelemetry-api-beta/src/main/java/io/opentelemetry/auto/instrumentation/opentelemetryapi/trace/UnshadedTracerProvider.java
instrumentation/opentelemetry-api-beta/src/main/java/io/opentelemetry/auto/instrumentation/opentelemetryapi/trace/UnshadedTracerProvider.java
package io.opentelemetry.auto.instrumentation.opentelemetryapi.trace;
import io.opentelemetry.auto.bootstrap.ContextStore;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import unshaded.io.grpc.Context;
+import unshaded.io.opentelemetry.internal.Obfuscated;
import unshaded.io.opentelemetry.trace.Tracer;
import unshaded.io.opentelemetry.trace.TracerProvider;
-public class UnshadedTracerProvider implements TracerProvider {
+public class UnshadedTracerProvider implements TracerProvider, Obfuscated {
+
+ private static final Logger log = LoggerFactory.getLogger(UnshadedTracerProvider.class);
+
+ private static final AtomicBoolean messageAlreadyLogged = new AtomicBoolean();
private final ContextStore<Context, io.grpc.Context> contextStore;
+ private final TracerProvider originalTracerProvider;
- public UnshadedTracerProvider(ContextStore<Context, io.grpc.Context> contextStore) {
+ public UnshadedTracerProvider(
+ ContextStore<Context, io.grpc.Context> contextStore, TracerProvider originalTracerProvider) {
this.contextStore = contextStore;
+ this.originalTracerProvider = originalTracerProvider;
}
@Override
@@ -42,4 +53,20 @@ public class UnshadedTracerProvider implements TracerProvider {
.get(instrumentationName, instrumentationVersion),
contextStore);
}
+
+ // this is called by OpenTelemetrySdk, which expects to get back a real TracerProviderSdk
+ @Override
+ public Object unobfuscate() {
+ if (!messageAlreadyLogged.getAndSet(true)) {
+ String message =
+ "direct usage of the OpenTelemetry SDK is not supported when running agent"
+ + " (run with debug logging to see stack trace)";
+ if (log.isDebugEnabled()) {
+ log.debug(message, new Exception("stack trace"));
+ } else {
+ log.info(message);
+ }
+ }
+ return ((Obfuscated<?>) originalTracerProvider).unobfuscate();
+ }
}

ADD
instrumentation/opentelemetry-api-beta/src/test/groovy/TracerSdkTest.groovy
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import io.opentelemetry.auto.test.AgentTestRunner
+import unshaded.io.opentelemetry.sdk.OpenTelemetrySdk
+import unshaded.io.opentelemetry.sdk.trace.TracerSdkProvider
+
+class TracerSdkTest extends AgentTestRunner {
+
+ def "direct access to sdk should not fail"() {
+ when:
+ def provider = OpenTelemetrySdk.getTracerProvider()
+
+ then:
+ provider instanceof TracerSdkProvider
+ }
+}

ADD
opentelemetry-sdk-beta-shaded-for-instrumenting/opentelemetry-sdk-beta-shaded-for-instrumenting.gradle
+plugins {
+ id "com.github.johnrengelman.shadow"
+}
+
+apply from: "$rootDir/gradle/java.gradle"
+
+dependencies {
+ implementation deps.opentelemetrySdk
+}
+
+// OpenTelemetry API (and SDK) shaded so that it can be used (and tested) in
+// opentelemetry-api-beta instrumentation, and then its usage can be unshaded after
+// OpenTelemetry API is shaded (see more explanation in opentelemetry-api-beta.gradle)
+shadowJar {
+
+ mergeServiceFiles()
+
+ relocate "io.opentelemetry", "unshaded.io.opentelemetry"
+ relocate "io.grpc", "unshaded.io.grpc"
+}

MODIFY
settings.gradle
settings.gradle
@@ -22,6 +22,7 @@ rootProject.name = 'opentelemetry-java-instrumentation'
include ':opentelemetry-javaagent'
include ':opentelemetry-sdk-shaded-for-testing'
include ':opentelemetry-api-beta-shaded-for-instrumenting'
+include ':opentelemetry-sdk-beta-shaded-for-instrumenting'
include ':agent-bootstrap'
include ':agent-tooling'
include ':load-generator'

Avoid ClassCastException on direct SDK access (#931)