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 Jun 11, 2019
1 parent 50a2db4 commit d641a16
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 51 deletions.
122 changes: 99 additions & 23 deletions api/src/main/java/io/opentelemetry/OpenTelemetry.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@
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;

/**
* 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 @@ -42,34 +44,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 @@ -78,30 +86,65 @@ 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
*/
private static <T> T loadSpiAndCheckSpecified(
Class<T> providerClass, final ClassLoader classLoader) {
T provider = loadSpi(providerClass, classLoader);
String specifiedProvider = System.getProperty(providerClass.getName());
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) {
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);
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("Tracer %s not found", specifiedProvider));
}
return null;
}

Expand All @@ -118,16 +161,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 @@ -38,5 +38,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 @@ -31,10 +31,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 @@ -31,10 +31,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();
}
42 changes: 28 additions & 14 deletions api/src/test/java/io/opentelemetry/OpenTelemetryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.opentelemetry.context.propagation.HttpTextFormat;
import io.opentelemetry.distributedcontext.DefaultDistributedContextManager;
import io.opentelemetry.distributedcontext.DistributedContext;
import io.opentelemetry.distributedcontext.DistributedContext.Builder;
import io.opentelemetry.distributedcontext.DistributedContextManager;
import io.opentelemetry.distributedcontext.spi.DistributedContextManagerProvider;
import io.opentelemetry.metrics.CounterDouble;
Expand Down Expand Up @@ -151,6 +152,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 @@ -189,7 +191,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 @@ -203,16 +205,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;
}

@Override
Expand Down Expand Up @@ -245,16 +251,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;
}

@Override
Expand Down Expand Up @@ -294,17 +304,21 @@ public void record(
}

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;
}

@Override
Expand All @@ -313,7 +327,7 @@ public DistributedContext getCurrentContext() {
}

@Override
public DistributedContext.Builder contextBuilder() {
public Builder contextBuilder() {
return null;
}

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());
}
}
Loading

0 comments on commit d641a16

Please sign in to comment.