diff --git a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java index 84a28fa789..9d40e939b6 100644 --- a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java +++ b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java @@ -14,6 +14,7 @@ import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; /** * A global singleton for the entrypoint to telemetry functionality for tracing, metrics and @@ -33,7 +34,7 @@ public final class GlobalOpenTelemetry { private static final Object mutex = new Object(); - @Nullable private static volatile OpenTelemetry globalOpenTelemetry; + @Nullable private static volatile ObfuscatedOpenTelemetry globalOpenTelemetry; @GuardedBy("mutex") @Nullable @@ -86,7 +87,7 @@ public final class GlobalOpenTelemetry { + "instead. Previous invocation set to cause of this exception.", setGlobalCaller); } - globalOpenTelemetry = openTelemetry; + globalOpenTelemetry = new ObfuscatedOpenTelemetry(openTelemetry); setGlobalCaller = new Throwable(); } } @@ -166,4 +167,29 @@ public final class GlobalOpenTelemetry { return null; } } + + /** + * Static global instances are obfuscated when they are returned from the API to prevent users + * from casting them to their SDK-specific implementation. For example, we do not want users to + * use patterns like {@code (OpenTelemetrySdk) GlobalOpenTelemetry.get()}. + */ + @ThreadSafe + static class ObfuscatedOpenTelemetry implements OpenTelemetry { + + private final OpenTelemetry delegate; + + ObfuscatedOpenTelemetry(OpenTelemetry delegate) { + this.delegate = delegate; + } + + @Override + public TracerProvider getTracerProvider() { + return delegate.getTracerProvider(); + } + + @Override + public ContextPropagators getPropagators() { + return delegate.getPropagators(); + } + } } diff --git a/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java b/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java index cb05df2284..0627c71624 100644 --- a/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java +++ b/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.InstanceOfAssertFactories.type; import static org.mockito.Mockito.mock; @@ -43,11 +44,29 @@ class OpenTelemetrySdkTest { @Test void testRegisterGlobal() { - OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().buildAndRegisterGlobal(); - assertThat(sdk).isSameAs(GlobalOpenTelemetry.get()); - assertThat(GlobalOpenTelemetry.get()).isSameAs(sdk); - assertThat(((OpenTelemetrySdk) GlobalOpenTelemetry.get()).getSdkTracerProvider().get("")) - .isSameAs(GlobalOpenTelemetry.getTracerProvider().get("")); + OpenTelemetrySdk sdk = + OpenTelemetrySdk.builder().setPropagators(propagators).buildAndRegisterGlobal(); + assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isSameAs(sdk); + assertThat(sdk.getTracerProvider().get("")) + .isSameAs(GlobalOpenTelemetry.getTracerProvider().get("")) + .isSameAs(GlobalOpenTelemetry.get().getTracer("")); + + assertThat(GlobalOpenTelemetry.getPropagators()) + .isSameAs(GlobalOpenTelemetry.get().getPropagators()) + .isSameAs(sdk.getPropagators()) + .isSameAs(propagators); + } + + @Test + void castingGlobalToSdkFails() { + OpenTelemetrySdk.builder().buildAndRegisterGlobal(); + + assertThatThrownBy( + () -> { + @SuppressWarnings("unused") + OpenTelemetrySdk shouldFail = (OpenTelemetrySdk) GlobalOpenTelemetry.get(); + }) + .isInstanceOf(ClassCastException.class); } @Test