Skip to content

Commit

Permalink
Add ReconfigurableOpenTelemetry to the otel api plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
cyrille-leclerc committed Jul 5, 2024
1 parent 508a0f7 commit d6b8758
Show file tree
Hide file tree
Showing 17 changed files with 4,627 additions and 4 deletions.
78 changes: 75 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<gitHubRepo>jenkinsci/opentelemetry-api-plugin</gitHubRepo>
<opentelemetry.version>1.39.0</opentelemetry.version>
<opentelemetry-instrumentation.version>2.5.0</opentelemetry-instrumentation.version>
<opentelemetry-semconv>1.25.0-alpha</opentelemetry-semconv>
<opentelemetry-semconv.version>1.25.0-alpha</opentelemetry-semconv.version>

<jenkins.version>2.440.3</jenkins.version>

Expand All @@ -50,17 +50,76 @@
<dependency>
<groupId>io.opentelemetry.semconv</groupId>
<artifactId>opentelemetry-semconv</artifactId>
<version>${opentelemetry-semconv}</version>
<version>${opentelemetry-semconv.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.semconv</groupId>
<artifactId>opentelemetry-semconv-incubating</artifactId>
<version>${opentelemetry-semconv}</version>
<version>${opentelemetry-semconv.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-api-incubator</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure-spi</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-logging</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<exclusions>
<exclusion>
<!-- we get okhttp from io.jenkins.plugins:okhttp-api -->
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
<exclusion>
<!-- we get okhttp from io.jenkins.plugins:okhttp-api -->
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>okhttp-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-runtime-telemetry-java8</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>

<dependencyManagement>
Expand Down Expand Up @@ -103,6 +162,19 @@
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.jenkins-ci.tools</groupId>
<artifactId>maven-hpi-plugin</artifactId>
<configuration>
<loggers>
</loggers>
</configuration>
</plugin>
</plugins>
</build>

<scm child.scm.connection.inherit.append.path="false" child.scm.developerConnection.inherit.append.path="false" child.scm.url.inherit.append.path="false">
<connection>scm:git:https://github.com/${gitHubRepo}</connection>
<developerConnection>scm:git:https://github.com/${gitHubRepo}</developerConnection>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.jenkins.plugins.opentelemetry.api;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionPoint;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.incubator.events.EventLoggerBuilder;
import io.opentelemetry.api.incubator.events.EventLoggerProvider;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.resources.Resource;

import java.util.Map;

public interface ExtendedOpenTelemetry extends ExtensionPoint, OpenTelemetry {
EventLoggerProvider getEventLoggerProvider();
EventLoggerBuilder eventLoggerBuilder(String instrumentationScopeName);
ConfigProperties getConfig();
Resource getResource();
void configure(@NonNull Map<String, String> openTelemetryProperties, Resource openTelemetryResource);

@Deprecated
OpenTelemetry getImplementation();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright The Original Author or Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.jenkins.plugins.opentelemetry.api;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;

/**
* <a href="https://opentelemetry.io/docs/concepts/instrumentation-scope/">OpenTelemetry instrumentation scope</a>,
* data structured used by the {@link ReconfigurableOpenTelemetry} implementation
*/
class InstrumentationScope {
@Nonnull
final String instrumentationScopeName;
@Nullable
final String schemaUrl;
@Nullable
final String instrumentationScopeVersion;

public InstrumentationScope(String instrumentationScopeName, @Nullable String schemaUrl, @Nullable String instrumentationScopeVersion) {
this.instrumentationScopeName = Objects.requireNonNull(instrumentationScopeName);
this.schemaUrl = schemaUrl;
this.instrumentationScopeVersion = instrumentationScopeVersion;
}

public InstrumentationScope(@Nonnull String instrumentationScopeName) {
this.instrumentationScopeName = instrumentationScopeName;
this.schemaUrl = null;
this.instrumentationScopeVersion = null;
}

@Override
public String toString() {
return "InstrumentationScope{" +
"instrumentationScopeName='" + instrumentationScopeName + '\'' +
", schemaUrl='" + schemaUrl + '\'' +
", instrumentationScopeVersion='" + instrumentationScopeVersion + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InstrumentationScope that = (InstrumentationScope) o;
return Objects.equals(instrumentationScopeName, that.instrumentationScopeName) && Objects.equals(schemaUrl, that.schemaUrl) && Objects.equals(instrumentationScopeVersion, that.instrumentationScopeVersion);
}

@Override
public int hashCode() {
return Objects.hash(instrumentationScopeName, schemaUrl, instrumentationScopeVersion);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright The Original Author or Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.jenkins.plugins.opentelemetry.api;

import hudson.ExtensionPoint;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;

public interface OpenTelemetryLifecycleListener extends ExtensionPoint, Comparable<OpenTelemetryLifecycleListener> {

default void afterConfiguration(ConfigProperties configProperties){}

/**
* @return the ordinal of this otel component to execute step handlers in predictable order. The smallest ordinal is handled first.
*/
default int ordinal() {
return 0;
}

@Override
default int compareTo(OpenTelemetryLifecycleListener other) {
if (this.ordinal() == other.ordinal()) {
return this.getClass().getName().compareTo(other.getClass().getName());
} else {
return Integer.compare(this.ordinal(), other.ordinal());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright The Original Author or Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.jenkins.plugins.opentelemetry.api;

import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.api.incubator.events.EventBuilder;
import io.opentelemetry.api.incubator.events.EventLogger;
import io.opentelemetry.api.incubator.events.EventLoggerBuilder;
import io.opentelemetry.api.incubator.events.EventLoggerProvider;

import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* <p>
* A {@link EventLoggerProvider} that allows to reconfigure the {@link EventLogger}s.
* </p>
* <p>
* We need reconfigurability because Jenkins supports changing the configuration of the OpenTelemetry params at runtime.
* All instantiated eventLoggers are reconfigured when the configuration changes, when
* {@link ReconfigurableEventLoggerProvider#setDelegate(EventLoggerProvider)} is invoked.
* </p>
*/
class ReconfigurableEventLoggerProvider implements EventLoggerProvider {

private final ConcurrentMap<InstrumentationScope, ReconfigurableEventLogger> eventLoggers = new ConcurrentHashMap<>();
private EventLoggerProvider delegate = EventLoggerProvider.noop();

@Override
public EventLoggerBuilder eventLoggerBuilder(String instrumentationScopeName) {
return new ReconfigurableEventLoggerBuilder(delegate.eventLoggerBuilder(instrumentationScopeName), instrumentationScopeName);
}

@Override
public EventLogger get(String instrumentationScopeName) {
return eventLoggers.computeIfAbsent(new InstrumentationScope(instrumentationScopeName), key -> new ReconfigurableEventLogger(delegate.get(key.instrumentationScopeName)));
}

public void setDelegate(EventLoggerProvider delegateEventLoggerBuilder) {
this.delegate = delegateEventLoggerBuilder;
eventLoggers.forEach((key, reconfigurableEventLogger) -> {
EventLoggerBuilder eventLoggerBuilder = delegateEventLoggerBuilder.eventLoggerBuilder(key.instrumentationScopeName);
Optional.ofNullable(key.schemaUrl).ifPresent(eventLoggerBuilder::setSchemaUrl);
Optional.ofNullable(key.instrumentationScopeVersion).ifPresent(eventLoggerBuilder::setInstrumentationVersion);
reconfigurableEventLogger.delegateEventLogger = eventLoggerBuilder.build();
});
}

@VisibleForTesting
protected static class ReconfigurableEventLogger implements EventLogger {
EventLogger delegateEventLogger;

public ReconfigurableEventLogger(EventLogger delegateEventLogger) {
this.delegateEventLogger = Objects.requireNonNull(delegateEventLogger);
}

@Override
public EventBuilder builder(String eventName) {
return delegateEventLogger.builder(eventName);
}
}

@VisibleForTesting
protected class ReconfigurableEventLoggerBuilder implements EventLoggerBuilder {
EventLoggerBuilder delegate;
String instrumentationScopeName;
String schemaUrl;
String instrumentationScopeVersion;

public ReconfigurableEventLoggerBuilder(EventLoggerBuilder delegate, String instrumentationScopeName) {
this.delegate = Objects.requireNonNull(delegate);
this.instrumentationScopeName = Objects.requireNonNull(instrumentationScopeName);
}

@Override
public EventLoggerBuilder setSchemaUrl(String schemaUrl) {
delegate.setSchemaUrl(schemaUrl);
this.schemaUrl = schemaUrl;
return this;
}

@Override
public EventLoggerBuilder setInstrumentationVersion(String instrumentationScopeVersion) {
delegate.setInstrumentationVersion(instrumentationScopeVersion);
this.instrumentationScopeVersion = instrumentationScopeVersion;
return this;
}

@Override
public EventLogger build() {
InstrumentationScope key = new InstrumentationScope(instrumentationScopeName, schemaUrl, instrumentationScopeVersion);
return eventLoggers.computeIfAbsent(key, k -> new ReconfigurableEventLogger(delegate.build()));
}
}
}
Loading

0 comments on commit d6b8758

Please sign in to comment.