From 7bddc58f7fea45b9ed5eadc0e78ae41694bc949b Mon Sep 17 00:00:00 2001 From: Pavol Loffay Date: Mon, 27 May 2019 14:32:00 +0200 Subject: [PATCH] Allow multiple instances of OpenTelemetry primary objects Signed-off-by: Pavol Loffay --- .../java/io/opentelemetry/OpenTelemetry.java | 124 ++++++++++++++---- .../DistributedContextManagerProvider.java | 2 +- .../metrics/spi/MeterProvider.java | 5 +- .../trace/spi/TracerProvider.java | 6 +- .../io/opentelemetry/OpenTelemetryTest.java | 39 ++++-- .../DistributedContextManagerSdkProvider.java | 5 +- .../sdk/metrics/MeterSdkProvider.java | 5 +- .../sdk/trace/TracerSdkProvider.java | 5 +- ...tributedContextManagerSdkProviderTest.java | 4 +- .../sdk/metrics/MeterSdkProviderTest.java | 3 +- .../sdk/trace/TracerSdkProviderTest.java | 3 +- 11 files changed, 150 insertions(+), 51 deletions(-) diff --git a/api/src/main/java/io/opentelemetry/OpenTelemetry.java b/api/src/main/java/io/opentelemetry/OpenTelemetry.java index 74049c8e8d3..c264913a023 100644 --- a/api/src/main/java/io/opentelemetry/OpenTelemetry.java +++ b/api/src/main/java/io/opentelemetry/OpenTelemetry.java @@ -25,6 +25,8 @@ import io.opentelemetry.trace.DefaultTracer; import io.opentelemetry.trace.Tracer; import io.opentelemetry.trace.spi.TracerProvider; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ServiceLoader; import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; @@ -33,7 +35,7 @@ * This class provides a static global accessor for telemetry objects {@link Tracer}, {@link Meter} * and {@link DistributedContextManager}. * - *

The telemetry objects are lazy-loaded singletons resolved via {@link ServiceLoader} mechanism. + *

The telemetry objects are resolved via {@link ServiceLoader} mechanism. * * @see TracerProvider * @see MeterProvider @@ -44,34 +46,40 @@ public final class OpenTelemetry { @Nullable private static volatile OpenTelemetry instance; - private final Tracer tracer; - private final Meter meter; - private final DistributedContextManager contextManager; + private final DistributedContextManagerProvider contextManagerProvider; + private final TracerProvider tracerProvider; + private final MeterProvider meterProvider; /** - * Returns a singleton {@link Tracer}. + * Returns an instance of {@link Tracer}. In a single deployment runtime a singleton is returned, + * however in an application server with multiple deployments a different instance can be used per + * deployment. * * @return registered tracer or default via {@link DefaultTracer#getInstance()}. * @throws IllegalStateException if a specified tracer (via system properties) could not be found. * @since 0.1.0 */ public static Tracer getTracer() { - return getInstance().tracer; + return getInstance().tracerProvider.get(); } /** - * Returns a singleton {@link Meter}. + * Returns an instance {@link Meter}. In a single deployment runtime a singleton is returned, + * however in an application server with multiple deployments a different instance can be used per + * deployment. * * @return registered meter or default via {@link DefaultMeter#getInstance()}. * @throws IllegalStateException if a specified meter (via system properties) could not be found. * @since 0.1.0 */ public static Meter getMeter() { - return getInstance().meter; + return getInstance().meterProvider.get(); } /** - * Returns a singleton {@link DistributedContextManager}. + * Returns an instance of {@link DistributedContextManager}. In a single deployment runtime a + * singleton is returned, however in an application server with multiple deployments a different + * instance can be used per deployment. * * @return registered manager or default via {@link * DefaultDistributedContextManager#getInstance()}. @@ -80,7 +88,7 @@ public static Meter getMeter() { * @since 0.1.0 */ public static DistributedContextManager getDistributedContextManager() { - return getInstance().contextManager; + return getInstance().contextManagerProvider.get(); } /** @@ -88,23 +96,58 @@ public static DistributedContextManager getDistributedContextManager() { * setting a system property with FQCN. * * @param providerClass a provider class + * @param classLoader class loader * @param provider type * @return a provider or null if not found * @throws IllegalStateException if a specified provider is not found */ @Nullable - private static T loadSpi(Class providerClass) { + private static T loadSpiAndCheckSpecified( + Class providerClass, final ClassLoader classLoader) { + T provider = loadSpi(providerClass, classLoader); String specifiedProvider = System.getProperty(providerClass.getName()); - ServiceLoader providers = ServiceLoader.load(providerClass); + if (specifiedProvider != null + && !specifiedProvider.equals(provider != null ? provider.getClass().getName() : null)) { + throw new IllegalStateException(String.format("Tracer %s not found", specifiedProvider)); + } + return provider; + } + + /** + * Load provider class via {@link ServiceLoader}. + * + * @param providerClass a provider class + * @param classLoader class loader + * @param provider type + */ + @Nullable + private static T loadSpi(Class providerClass, final ClassLoader classLoader) { + if (classLoader == null) { + return null; + } + + ClassLoader parentCl = + AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public ClassLoader run() { + return classLoader.getParent(); + } + }); + // providers packaged in application runtimes have a higher priority + // than ones packaged in the application archives + T t = loadSpi(providerClass, parentCl); + if (t != null) { + return t; + } + + String specifiedProvider = System.getProperty(providerClass.getName()); + ServiceLoader providers = ServiceLoader.load(providerClass, classLoader); for (T provider : providers) { if (specifiedProvider == null || specifiedProvider.equals(provider.getClass().getName())) { return provider; } } - if (specifiedProvider != null) { - throw new IllegalStateException( - String.format("Service provider %s not found", specifiedProvider)); - } return null; } @@ -121,16 +164,49 @@ private static OpenTelemetry getInstance() { } private OpenTelemetry() { - TracerProvider tracerProvider = loadSpi(TracerProvider.class); - tracer = tracerProvider != null ? tracerProvider.create() : DefaultTracer.getInstance(); - MeterProvider meterProvider = loadSpi(MeterProvider.class); - meter = meterProvider != null ? meterProvider.create() : DefaultMeter.getInstance(); + ClassLoader cl = + AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public ClassLoader run() { + return Thread.currentThread().getContextClassLoader(); + } + }); + if (cl == null) { + cl = OpenTelemetry.class.getClassLoader(); + } + + TracerProvider tracerProvider = loadSpiAndCheckSpecified(TracerProvider.class, cl); + this.tracerProvider = + tracerProvider != null + ? tracerProvider + : new TracerProvider() { + @Override + public Tracer get() { + return DefaultTracer.getInstance(); + } + }; + MeterProvider meterProvider = loadSpiAndCheckSpecified(MeterProvider.class, cl); + this.meterProvider = + meterProvider != null + ? meterProvider + : new MeterProvider() { + @Override + public Meter get() { + return DefaultMeter.getInstance(); + } + }; DistributedContextManagerProvider contextManagerProvider = - loadSpi(DistributedContextManagerProvider.class); - contextManager = + loadSpiAndCheckSpecified(DistributedContextManagerProvider.class, cl); + this.contextManagerProvider = contextManagerProvider != null - ? contextManagerProvider.create() - : DefaultDistributedContextManager.getInstance(); + ? contextManagerProvider + : new DistributedContextManagerProvider() { + @Override + public DistributedContextManager get() { + return DefaultDistributedContextManager.getInstance(); + } + }; } // for testing diff --git a/api/src/main/java/io/opentelemetry/distributedcontext/spi/DistributedContextManagerProvider.java b/api/src/main/java/io/opentelemetry/distributedcontext/spi/DistributedContextManagerProvider.java index 637ece87035..3c0ef6d9451 100644 --- a/api/src/main/java/io/opentelemetry/distributedcontext/spi/DistributedContextManagerProvider.java +++ b/api/src/main/java/io/opentelemetry/distributedcontext/spi/DistributedContextManagerProvider.java @@ -40,5 +40,5 @@ public interface DistributedContextManagerProvider { * @return a {@code DistributedContextManager} instance. * @since 0.1.0 */ - DistributedContextManager create(); + DistributedContextManager get(); } diff --git a/api/src/main/java/io/opentelemetry/metrics/spi/MeterProvider.java b/api/src/main/java/io/opentelemetry/metrics/spi/MeterProvider.java index 5db6dd7dbc8..08d101d4bc0 100644 --- a/api/src/main/java/io/opentelemetry/metrics/spi/MeterProvider.java +++ b/api/src/main/java/io/opentelemetry/metrics/spi/MeterProvider.java @@ -33,10 +33,11 @@ public interface MeterProvider { /** - * Creates a new meter instance. + * Creates a meter instance. In a single deployment runtime a singleton is returned, however in an + * application server with multiple deployments a different instance can be used per deployment. * * @return a meter instance. * @since 0.1.0 */ - Meter create(); + Meter get(); } diff --git a/api/src/main/java/io/opentelemetry/trace/spi/TracerProvider.java b/api/src/main/java/io/opentelemetry/trace/spi/TracerProvider.java index 08949bb1a52..0e94867138c 100644 --- a/api/src/main/java/io/opentelemetry/trace/spi/TracerProvider.java +++ b/api/src/main/java/io/opentelemetry/trace/spi/TracerProvider.java @@ -33,10 +33,12 @@ public interface TracerProvider { /** - * Creates a new tracer instance. + * Returns a tracer instance. In a single deployment runtime a singleton is returned, however in + * an application server with multiple deployments a different instance can be used per + * deployment. * * @return a tracer instance. * @since 0.1.0 */ - Tracer create(); + Tracer get(); } diff --git a/api/src/test/java/io/opentelemetry/OpenTelemetryTest.java b/api/src/test/java/io/opentelemetry/OpenTelemetryTest.java index 50eb33ba314..9069df6f869 100644 --- a/api/src/test/java/io/opentelemetry/OpenTelemetryTest.java +++ b/api/src/test/java/io/opentelemetry/OpenTelemetryTest.java @@ -157,6 +157,7 @@ public void testDistributedContextManagerLoadArbitrary() throws IOException { FirstDistributedContextManager.class, SecondDistributedContextManager.class); try { + System.out.println(OpenTelemetry.getDistributedContextManager()); assertTrue( (OpenTelemetry.getDistributedContextManager() instanceof FirstDistributedContextManager) || (OpenTelemetry.getDistributedContextManager() @@ -196,7 +197,7 @@ public void testDistributedContextManagerNotFound() { } private static File createService(Class service, Class... impls) throws IOException { - URL location = Tracer.class.getProtectionDomain().getCodeSource().getLocation(); + URL location = OpenTelemetryTest.class.getProtectionDomain().getCodeSource().getLocation(); File file = new File(location.getPath() + "META-INF/services/" + service.getName()); file.getParentFile().mkdirs(); @@ -211,16 +212,20 @@ private static File createService(Class service, Class... impls) throws IO } public static class SecondTracer extends FirstTracer { + private static final Tracer INSTANCE = new SecondTracer(); + @Override - public Tracer create() { - return new SecondTracer(); + public Tracer get() { + return INSTANCE; } } public static class FirstTracer implements Tracer, TracerProvider { + private static final Tracer INSTANCE = new FirstTracer(); + @Override - public Tracer create() { - return new FirstTracer(); + public Tracer get() { + return INSTANCE; } @Nullable @@ -255,16 +260,20 @@ public HttpTextFormat getHttpTextFormat() { } public static class SecondMeter extends FirstMeter { + private static final Meter INSTANCE = new SecondMeter(); + @Override - public Meter create() { - return new SecondMeter(); + public Meter get() { + return INSTANCE; } } public static class FirstMeter implements Meter, MeterProvider { + private static final Meter INSTANCE = new FirstMeter(); + @Override - public Meter create() { - return new FirstMeter(); + public Meter get() { + return INSTANCE; } @Nullable @@ -311,17 +320,21 @@ public MeasureBatchRecorder newMeasureBatchRecorder() { } public static class SecondDistributedContextManager extends FirstDistributedContextManager { + private static final DistributedContextManager INSTANCE = new SecondDistributedContextManager(); + @Override - public DistributedContextManager create() { - return new SecondDistributedContextManager(); + public DistributedContextManager get() { + return INSTANCE; } } public static class FirstDistributedContextManager implements DistributedContextManager, DistributedContextManagerProvider { + private static final DistributedContextManager INSTANCE = new FirstDistributedContextManager(); + @Override - public DistributedContextManager create() { - return new FirstDistributedContextManager(); + public DistributedContextManager get() { + return INSTANCE; } @Nullable diff --git a/sdk/src/main/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProvider.java b/sdk/src/main/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProvider.java index a0ba4e368f3..43f72aecf43 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProvider.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProvider.java @@ -27,9 +27,10 @@ * io.opentelemetry.OpenTelemetry}. */ public class DistributedContextManagerSdkProvider implements DistributedContextManagerProvider { + private static final DistributedContextManager INSTANCE = new DistributedContextManagerSdk(); @Override - public DistributedContextManager create() { - return new DistributedContextManagerSdk(); + public DistributedContextManager get() { + return INSTANCE; } } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/metrics/MeterSdkProvider.java b/sdk/src/main/java/io/opentelemetry/sdk/metrics/MeterSdkProvider.java index f9fa4c1f9fe..09d9f9eb87a 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/metrics/MeterSdkProvider.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/metrics/MeterSdkProvider.java @@ -26,9 +26,10 @@ * io.opentelemetry.OpenTelemetry}. */ public class MeterSdkProvider implements MeterProvider { + private static final MeterSdk INSTANCE = new MeterSdk(); @Override - public Meter create() { - return new MeterSdk(); + public Meter get() { + return INSTANCE; } } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/TracerSdkProvider.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/TracerSdkProvider.java index faf3271819d..8caddc045bd 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/TracerSdkProvider.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/TracerSdkProvider.java @@ -25,9 +25,10 @@ * io.opentelemetry.OpenTelemetry}. */ public class TracerSdkProvider implements io.opentelemetry.trace.spi.TracerProvider { + private static final TracerSdk INSTANCE = new TracerSdk(); @Override - public Tracer create() { - return new TracerSdk(); + public Tracer get() { + return INSTANCE; } } diff --git a/sdk/src/test/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProviderTest.java b/sdk/src/test/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProviderTest.java index fb49e787d94..7054fe91536 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProviderTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/distributedcontext/DistributedContextManagerSdkProviderTest.java @@ -27,7 +27,9 @@ public class DistributedContextManagerSdkProviderTest { @Test public void testDefault() { - assertThat(new DistributedContextManagerSdkProvider().create()) + assertThat(new DistributedContextManagerSdkProvider().get()) .isInstanceOf(DistributedContextManagerSdk.class); + assertThat(new DistributedContextManagerSdkProvider().get()) + .isSameInstanceAs(new DistributedContextManagerSdkProvider().get()); } } diff --git a/sdk/src/test/java/io/opentelemetry/sdk/metrics/MeterSdkProviderTest.java b/sdk/src/test/java/io/opentelemetry/sdk/metrics/MeterSdkProviderTest.java index 1e7059aea61..b0f4ca9dc1a 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/metrics/MeterSdkProviderTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/metrics/MeterSdkProviderTest.java @@ -27,6 +27,7 @@ public class MeterSdkProviderTest { @Test public void testDefault() { - assertThat(new MeterSdkProvider().create()).isInstanceOf(MeterSdk.class); + assertThat(new MeterSdkProvider().get()).isInstanceOf(MeterSdk.class); + assertThat(new MeterSdkProvider().get()).isSameInstanceAs(new MeterSdkProvider().get()); } } diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/TracerSdkProviderTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/TracerSdkProviderTest.java index c0431b979d8..430d734a857 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/TracerSdkProviderTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/TracerSdkProviderTest.java @@ -27,6 +27,7 @@ public class TracerSdkProviderTest { @Test public void testDefault() { - assertThat(new TracerSdkProvider().create()).isInstanceOf(TracerSdk.class); + assertThat(new TracerSdkProvider().get()).isInstanceOf(TracerSdk.class); + assertThat(new TracerSdkProvider().get()).isSameInstanceAs(new TracerSdkProvider().get()); } }