Skip to content

Commit

Permalink
Allow multiple instances of OpenTelemetry primary objects
Browse files Browse the repository at this point in the history
Signed-off-by: Pavol Loffay <[email protected]>
  • Loading branch information
pavolloffay committed Sep 11, 2019
1 parent 78e4e10 commit 7bddc58
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 51 deletions.
124 changes: 100 additions & 24 deletions api/src/main/java/io/opentelemetry/OpenTelemetry.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -33,7 +35,7 @@
* This class provides a static global accessor for telemetry objects {@link Tracer}, {@link Meter}
* and {@link DistributedContextManager}.
*
* <p>The telemetry objects are lazy-loaded singletons resolved via {@link ServiceLoader} mechanism.
* <p>The telemetry objects are resolved via {@link ServiceLoader} mechanism.
*
* @see TracerProvider
* @see MeterProvider
Expand All @@ -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()}.
Expand All @@ -80,31 +88,66 @@ public static Meter getMeter() {
* @since 0.1.0
*/
public static DistributedContextManager getDistributedContextManager() {
return getInstance().contextManager;
return getInstance().contextManagerProvider.get();
}

/**
* Load provider class via {@link ServiceLoader}. A specific provider class can be requested via
* setting a system property with FQCN.
*
* @param providerClass a provider class
* @param classLoader class loader
* @param <T> provider type
* @return a provider or null if not found
* @throws IllegalStateException if a specified provider is not found
*/
@Nullable
private static <T> T loadSpi(Class<T> providerClass) {
private static <T> T loadSpiAndCheckSpecified(
Class<T> providerClass, final ClassLoader classLoader) {
T provider = loadSpi(providerClass, classLoader);
String specifiedProvider = System.getProperty(providerClass.getName());
ServiceLoader<T> 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 <T> provider type
*/
@Nullable
private static <T> T loadSpi(Class<T> providerClass, final ClassLoader classLoader) {
if (classLoader == null) {
return null;
}

ClassLoader parentCl =
AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
@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<T> 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;
}

Expand All @@ -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<ClassLoader>() {
@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ public interface DistributedContextManagerProvider {
* @return a {@code DistributedContextManager} instance.
* @since 0.1.0
*/
DistributedContextManager create();
DistributedContextManager get();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
39 changes: 26 additions & 13 deletions api/src/test/java/io/opentelemetry/OpenTelemetryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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();

Expand All @@ -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
Expand Down Expand Up @@ -255,16 +260,20 @@ public HttpTextFormat<SpanContext> 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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

0 comments on commit 7bddc58

Please sign in to comment.