Skip to content

Commit

Permalink
Support using ReconfigurableOpenTelemetry instance through @Inject
Browse files Browse the repository at this point in the history
  • Loading branch information
cyrille-leclerc committed Aug 27, 2024
1 parent 83ee9ac commit 85785bc
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,48 @@
import io.opentelemetry.sdk.resources.Resource;

import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Supplier;

/**
* Extension of {@link OpenTelemetry} that provides additional functionality:
* <ul>
* <li>Accessor to the {@link EventLoggerProvider} without requiring using a separate {@link io.opentelemetry.api.incubator.events.GlobalEventLoggerProvider}</li>
* <li>Read access top the {@link ConfigProperties} and {@link Resource}</li>
* <li>Ability to be reconfigured through {@link #configure(Map, Resource, boolean)}</li>
* <li>Ability to be used as a Jenkins {@link hudson.Extension}</li>
* </ul>
*/
public interface ExtendedOpenTelemetry extends ExtensionPoint, OpenTelemetry {
EventLoggerProvider getEventLoggerProvider();

EventLoggerBuilder eventLoggerBuilder(String instrumentationScopeName);

/**
* {@link ConfigProperties} used to instantiate this OpenTelemetry instance using the {@link io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk}.
*/
ConfigProperties getConfig();

/**
* {@link Resource} used by this OpenTelemetry instance for the resource attributes of the produced telemetry
*/
Resource getResource();

/**
*
* @deprecated use {@link #configure(Map, Resource, boolean)}
*/
@Deprecated
void configure(@NonNull Map<String, String> openTelemetryProperties, Resource openTelemetryResource);
default void configure(@NonNull Map<String, String> openTelemetryProperties, Resource openTelemetryResource, boolean disableShutdownHook){}

/**
* Reconfigures the {@link OpenTelemetry} instance. If no exporter is explicitly defined, this OpenTelemetry instance is NoOp.
*
* @param openTelemetryProperties properties used as {@link ConfigProperties} through {@link io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder#addPropertiesSupplier(Supplier)}
* @param openTelemetryResource resource attributes passed through {@link io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder#addResourceCustomizer(BiFunction)}
* @param disableShutdownHook enable / disable a shutdown hook
*/
default void configure(@NonNull Map<String, String> openTelemetryProperties, Resource openTelemetryResource, boolean disableShutdownHook) {
}

Check warning on line 53 in src/main/java/io/jenkins/plugins/opentelemetry/api/ExtendedOpenTelemetry.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 53 is not covered by tests

@Deprecated
OpenTelemetry getImplementation();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.jenkins.plugins.opentelemetry.api;

import com.google.inject.AbstractModule;
import hudson.Extension;
import io.opentelemetry.api.OpenTelemetry;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Support using @{@link javax.inject.Inject} to inject the OpenTelemetry instance in Jenkins @{@link Extension}.
*/
@Extension
public class OpenTelemetryApiGuiceModule extends AbstractModule {
static final Logger logger = Logger.getLogger(OpenTelemetryApiGuiceModule.class.getName());

public OpenTelemetryApiGuiceModule() {
logger.log(Level.FINE, "Creating OpenTelemetryApiGuiceModule");
}

@Override
public void configure() {
logger.log(Level.FINE, "Configuring OpenTelemetryApiGuiceModule");
ReconfigurableOpenTelemetry reconfigurableOpenTelemetry = ReconfigurableOpenTelemetry.get();
bind(OpenTelemetry.class).toInstance(reconfigurableOpenTelemetry);
bind(ExtendedOpenTelemetry.class).toInstance(reconfigurableOpenTelemetry);
bind(ReconfigurableOpenTelemetry.class).toInstance(reconfigurableOpenTelemetry);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider;
Expand All @@ -41,24 +40,34 @@
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* <p>
* Reconfigurable {@link EventLoggerProvider} that allows to reconfigure the {@link Tracer}s,
* {@link io.opentelemetry.api.logs.Logger}s, and {@link EventLogger}s.
* Reconfigurable {@link OpenTelemetry}.
* </p>
* <p>
* We need reconfigurability because Jenkins supports changing the configuration of the OpenTelemetry params at runtime.
* All instantiated tracers, loggers, and eventLoggers are reconfigured when the configuration changes, when
* {@link ReconfigurableOpenTelemetry#configure(Map, Resource)} is invoked.
* </p>
* <p>
* Reconfigurable {@link EventLoggerProvider} that allows to reconfigure the {@link Tracer}s,
* {@link io.opentelemetry.api.logs.Logger}s, and {@link EventLogger}s.
* </p>
* <p>
* Jenkins components interested in being notified after the OpenTelemetry configuration changes can be marked as @{@link Extension}
* and implement {@link OpenTelemetryLifecycleListener}.
* </p>
*/
@Extension(ordinal = Integer.MAX_VALUE)
public class ReconfigurableOpenTelemetry implements ExtendedOpenTelemetry, OpenTelemetry, Closeable, ExtensionPoint {

protected final Logger logger = Logger.getLogger(getClass().getName());
private final static Logger logger = Logger.getLogger(ReconfigurableOpenTelemetry.class.getName());
private final static ReconfigurableOpenTelemetry INSTANCE = new ReconfigurableOpenTelemetry();
private final static AtomicInteger GET_INVOCATION_COUNT = new AtomicInteger(0);

Resource resource = Resource.empty();
ConfigProperties config = ConfigPropertiesUtils.emptyConfig();
OpenTelemetry openTelemetryImpl = OpenTelemetry.noop();
Expand All @@ -68,8 +77,39 @@ public class ReconfigurableOpenTelemetry implements ExtendedOpenTelemetry, OpenT
final ReconfigurableLoggerProvider loggerProviderImpl = new ReconfigurableLoggerProvider();
final ReconfigurableEventLoggerProvider eventLoggerProviderImpl = new ReconfigurableEventLoggerProvider();

/*
* Ensures this class is loaded and the static singleton `INSTANCE` is instantiated.
*/
@Initializer(after = InitMilestone.EXTENSIONS_AUGMENTED, before = InitMilestone.SYSTEM_CONFIG_LOADED)
public static void init() {
logger.log(Level.FINE, () -> "OpenTelemetry configured as NoOp: " + INSTANCE);
}

/**
* Initialize as NoOp
* Use a factory method for the @{@link Extension} to ensure single instantiation
* across Jenkins
* <p>
* The Jenkins component {@link ReconfigurableOpenTelemetry} is instantiated through the static factory method
* {@link #get()} rather than through the instance constructor to ensure that we have single
* instantiation across Jenkins' @{@link Extension} and Google Guice @{@link com.google.inject.Inject}.
* </p>
* <p>
* This {@link #get()} factory method works in conjunction with {@link OpenTelemetryApiGuiceModule}
* </p>
*/
@Extension(ordinal = Integer.MAX_VALUE)
public static ReconfigurableOpenTelemetry get() {
int getInvocationCount = GET_INVOCATION_COUNT.incrementAndGet();
logger.log(Level.FINE, () -> "get(invocationCount=" + getInvocationCount + "): " + INSTANCE);
return INSTANCE;
}

/**
* <p>
* Initialize as NoOp.
* </p>
* @see #get()
*/
public ReconfigurableOpenTelemetry() {
try {
Expand All @@ -88,13 +128,9 @@ public ReconfigurableOpenTelemetry() {
"GlobalEventLoggerProvide with instance " + Optional.of(GlobalEventLoggerProvider.get()).map(elp -> elp + "@" + System.identityHashCode(elp)));
}

@Initializer(after = InitMilestone.EXTENSIONS_AUGMENTED, before = InitMilestone.SYSTEM_CONFIG_LOADED)
public void init() {
logger.log(Level.INFO, "OpenTelemetry configured as NoOp");
}

/**
* Configure the OpenTelemetry SDK with the given properties and resource disabling the OTel SDK shutdown hook
* @deprecated use {@link #configure(Map, Resource, boolean)} instead
*/
@Deprecated
@Override
Expand Down Expand Up @@ -136,9 +172,9 @@ public void configure(@NonNull Map<String, String> openTelemetryProperties, Reso
.getOpenTelemetrySdk();
setOpenTelemetryImpl(openTelemetrySdk);

logger.log(Level.INFO, () -> "OpenTelemetry SDK configured: " + ConfigPropertiesUtils.prettyPrintOtelSdkConfig(this.config, this.resource));
logger.log(Level.INFO, () -> "OpenTelemetry configured: " + ConfigPropertiesUtils.prettyPrintOtelSdkConfig(this.config, this.resource));

if (disableShutdownHook) {
if (disableShutdownHook) {
if (shutdownHook == null) {
// nothing to do, no shutdownhook registered
} else {
Expand All @@ -159,7 +195,7 @@ public void configure(@NonNull Map<String, String> openTelemetryProperties, Reso
this.config = ConfigPropertiesUtils.emptyConfig();
setOpenTelemetryImpl(OpenTelemetry.noop());

logger.log(Level.INFO, "OpenTelemetry configured as NoOp");
logger.log(Level.FINE, () -> "OpenTelemetry configured as NoOp");

Check warning on line 198 in src/main/java/io/jenkins/plugins/opentelemetry/api/ReconfigurableOpenTelemetry.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 175-198 are not covered by tests
}

postOpenTelemetrySdkConfiguration();
Expand Down Expand Up @@ -276,18 +312,4 @@ protected void postOpenTelemetrySdkConfiguration() {
openTelemetryLifecycleListener.afterConfiguration(this.config);
});
}

static class ShutdownHook extends Thread {
final OpenTelemetrySdk openTelemetrySdk;

public ShutdownHook(OpenTelemetrySdk openTelemetrySdk) {
super("OpenTelemetry SDK Shutdown Hook");
this.openTelemetrySdk = openTelemetrySdk;
}

@Override
public void run() {
openTelemetrySdk.close();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ public class ApacheHttpClientInstrumentationTest {

@Test
public void test_instantiate_instrumented_http_client() {
ReconfigurableOpenTelemetry openTelemetry = new ReconfigurableOpenTelemetry();
HttpClientBuilder httpClientBuilder = ApacheHttpClientTelemetry.create(openTelemetry).newHttpClientBuilder();
CloseableHttpClient httpClient = httpClientBuilder.build();
System.out.println(httpClient);
try (ReconfigurableOpenTelemetry openTelemetry = new ReconfigurableOpenTelemetry()) {
HttpClientBuilder httpClientBuilder = ApacheHttpClientTelemetry.create(openTelemetry).newHttpClientBuilder();
CloseableHttpClient httpClient = httpClientBuilder.build();
System.out.println(httpClient);
}
}
}

0 comments on commit 85785bc

Please sign in to comment.