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 5141307c343..5e3a1e0ecdb 100644 --- a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java +++ b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java @@ -7,6 +7,7 @@ import io.opentelemetry.api.internal.ConfigUtil; import io.opentelemetry.api.internal.GuardedBy; +import io.opentelemetry.api.internal.IncubatingUtil; import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterBuilder; @@ -56,7 +57,7 @@ public final class GlobalOpenTelemetry { @SuppressWarnings("NonFinalStaticField") @Nullable - private static volatile ObfuscatedOpenTelemetry globalOpenTelemetry; + private static volatile OpenTelemetry globalOpenTelemetry; @SuppressWarnings("NonFinalStaticField") @GuardedBy("mutex") @@ -112,7 +113,7 @@ public static void set(OpenTelemetry openTelemetry) { + "instead. Previous invocation set to cause of this exception.", setGlobalCaller); } - globalOpenTelemetry = new ObfuscatedOpenTelemetry(openTelemetry); + globalOpenTelemetry = obfuscatedOpenTelemetry(openTelemetry); setGlobalCaller = new Throwable(); } } @@ -261,8 +262,7 @@ private static OpenTelemetry maybeAutoConfigureAndSetGlobal() { Object autoConfiguredSdk = initialize.invoke(null); Method getOpenTelemetrySdk = openTelemetrySdkAutoConfiguration.getMethod("getOpenTelemetrySdk"); - return new ObfuscatedOpenTelemetry( - (OpenTelemetry) getOpenTelemetrySdk.invoke(autoConfiguredSdk)); + return obfuscatedOpenTelemetry((OpenTelemetry) getOpenTelemetrySdk.invoke(autoConfiguredSdk)); } catch (NoSuchMethodException | IllegalAccessException e) { throw new IllegalStateException( "AutoConfiguredOpenTelemetrySdk detected on classpath " @@ -277,6 +277,14 @@ private static OpenTelemetry maybeAutoConfigureAndSetGlobal() { } } + private static OpenTelemetry obfuscatedOpenTelemetry(OpenTelemetry openTelemetry) { + OpenTelemetry incubating = IncubatingUtil.obfuscatedOpenTelemetryIfIncubating(openTelemetry); + if (incubating != null) { + return incubating; + } + return new ObfuscatedOpenTelemetry(openTelemetry); + } + /** * 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 diff --git a/api/all/src/main/java/io/opentelemetry/api/internal/IncubatingUtil.java b/api/all/src/main/java/io/opentelemetry/api/internal/IncubatingUtil.java index 1ef82d373f2..e03b1e22f8c 100644 --- a/api/all/src/main/java/io/opentelemetry/api/internal/IncubatingUtil.java +++ b/api/all/src/main/java/io/opentelemetry/api/internal/IncubatingUtil.java @@ -5,7 +5,9 @@ package io.opentelemetry.api.internal; +import io.opentelemetry.api.OpenTelemetry; import java.lang.reflect.Method; +import javax.annotation.Nullable; /** * Incubating utilities. @@ -26,4 +28,24 @@ public static T incubatingApiIfAvailable(T stableApi, String incubatingClass return stableApi; } } + + @Nullable + public static OpenTelemetry obfuscatedOpenTelemetryIfIncubating(OpenTelemetry openTelemetry) { + try { + Class extendedClass = + Class.forName("io.opentelemetry.api.incubator.ExtendedOpenTelemetry"); + if (extendedClass.isInstance(openTelemetry)) { + Class incubatingClass = + Class.forName( + "io.opentelemetry.api.incubator.internal.ObfuscatedExtendedOpenTelemetry"); + return (OpenTelemetry) + incubatingClass + .getDeclaredConstructor(extendedClass) + .newInstance(extendedClass.cast(openTelemetry)); + } + } catch (Exception e) { + // incubator not available + } + return null; + } } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/internal/ObfuscatedExtendedOpenTelemetry.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/internal/ObfuscatedExtendedOpenTelemetry.java new file mode 100644 index 00000000000..93a0b90ea91 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/internal/ObfuscatedExtendedOpenTelemetry.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.internal; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.trace.TracerBuilder; +import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.context.propagation.ContextPropagators; +import javax.annotation.concurrent.ThreadSafe; + +/** + * 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()}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@ThreadSafe +public final class ObfuscatedExtendedOpenTelemetry implements ExtendedOpenTelemetry { + + private final ExtendedOpenTelemetry delegate; + + /** + * This constructor is called via reflection from {@link + * io.opentelemetry.api.internal.IncubatingUtil#obfuscatedOpenTelemetryIfIncubating(OpenTelemetry)}. + */ + public ObfuscatedExtendedOpenTelemetry(ExtendedOpenTelemetry delegate) { + this.delegate = delegate; + } + + @Override + public TracerProvider getTracerProvider() { + return delegate.getTracerProvider(); + } + + @Override + public MeterProvider getMeterProvider() { + return delegate.getMeterProvider(); + } + + @Override + public LoggerProvider getLogsBridge() { + return delegate.getLogsBridge(); + } + + @Override + public ContextPropagators getPropagators() { + return delegate.getPropagators(); + } + + @Override + public TracerBuilder tracerBuilder(String instrumentationScopeName) { + return delegate.tracerBuilder(instrumentationScopeName); + } + + @Override + public ConfigProvider getConfigProvider() { + return delegate.getConfigProvider(); + } +} diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/ExtendedOpenTelemetryTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/ExtendedOpenTelemetryTest.java index 4903d00c2e6..651eae4bc14 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/ExtendedOpenTelemetryTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/ExtendedOpenTelemetryTest.java @@ -7,6 +7,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.incubator.logs.ExtendedDefaultLoggerProvider; import io.opentelemetry.api.incubator.logs.ExtendedLogger; @@ -19,6 +20,11 @@ import io.opentelemetry.api.testing.internal.AbstractOpenTelemetryTest; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.extension.incubator.ExtendedOpenTelemetrySdk; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ExtendedOpenTelemetryTest extends AbstractOpenTelemetryTest { @@ -38,12 +44,26 @@ protected LoggerProvider getLoggerProvider() { return ExtendedDefaultLoggerProvider.getNoop(); } + @BeforeEach + void setUp() { + GlobalOpenTelemetry.resetForTest(); + } + @Test void incubatingApiIsLoaded() { assertIsExtended(OpenTelemetry.noop()); assertIsExtended(OpenTelemetry.propagating(ContextPropagators.noop())); } + @Test + void globalOpenTelemetry() { + GlobalOpenTelemetry.set( + ExtendedOpenTelemetrySdk.create( + OpenTelemetrySdk.builder().build(), + SdkConfigProvider.create(new OpenTelemetryConfigurationModel()))); + assertThat(GlobalOpenTelemetry.get()).isInstanceOf(ExtendedOpenTelemetry.class); + } + private static void assertIsExtended(OpenTelemetry openTelemetry) { assertThat(openTelemetry.getMeter("test").counterBuilder("test")) .isInstanceOf(ExtendedLongCounterBuilder.class); diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/internal/ObfuscatedExtendedOpenTelemetryTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/internal/ObfuscatedExtendedOpenTelemetryTest.java new file mode 100644 index 00000000000..5752419bb78 --- /dev/null +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/internal/ObfuscatedExtendedOpenTelemetryTest.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.trace.TracerBuilder; +import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.context.propagation.ContextPropagators; +import org.junit.jupiter.api.Test; + +class ObfuscatedExtendedOpenTelemetryTest { + + @Test + void delegatesAllCallsToDelegate() { + ExtendedOpenTelemetry delegate = mock(ExtendedOpenTelemetry.class); + + TracerProvider tracerProvider = mock(TracerProvider.class); + MeterProvider meterProvider = mock(MeterProvider.class); + LoggerProvider loggerProvider = mock(LoggerProvider.class); + ContextPropagators propagators = mock(ContextPropagators.class); + TracerBuilder tracerBuilder = mock(TracerBuilder.class); + ConfigProvider configProvider = mock(ConfigProvider.class); + + when(delegate.getTracerProvider()).thenReturn(tracerProvider); + when(delegate.getMeterProvider()).thenReturn(meterProvider); + when(delegate.getLogsBridge()).thenReturn(loggerProvider); + when(delegate.getPropagators()).thenReturn(propagators); + when(delegate.tracerBuilder("test-instrumentation")).thenReturn(tracerBuilder); + when(delegate.getConfigProvider()).thenReturn(configProvider); + + ObfuscatedExtendedOpenTelemetry obfuscated = new ObfuscatedExtendedOpenTelemetry(delegate); + + assertThat(obfuscated.getTracerProvider()).isSameAs(tracerProvider); + verify(delegate).getTracerProvider(); + + assertThat(obfuscated.getMeterProvider()).isSameAs(meterProvider); + verify(delegate).getMeterProvider(); + + assertThat(obfuscated.getLogsBridge()).isSameAs(loggerProvider); + verify(delegate).getLogsBridge(); + + assertThat(obfuscated.getPropagators()).isSameAs(propagators); + verify(delegate).getPropagators(); + + assertThat(obfuscated.tracerBuilder("test-instrumentation")).isSameAs(tracerBuilder); + verify(delegate).tracerBuilder("test-instrumentation"); + + assertThat(obfuscated.getConfigProvider()).isSameAs(configProvider); + verify(delegate).getConfigProvider(); + } +}