Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Maven Extension] Migrate from Plexus to JSR 330 dependency injection APIs #1320

Merged
merged 6 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions maven-extension/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ plugins {
}

// NOTE
// `META-INF/plexus/components.xml` is manually handled under src/main/resources because there is no Gradle
// equivalent to the Maven plugin `plexus-component-metadata:generate-metadata`
// `META-INF/sis/javax.inject.Named` is manually handled under src/main/resources because there is
// no Gradle equivalent to the Maven plugin `org.eclipse.sisu:sisu-maven-plugin`

description = "Maven extension to observe Maven builds with distributed traces using OpenTelemetry SDK"
otelJava.moduleName.set("io.opentelemetry.maven")

dependencies {
implementation("org.codehaus.plexus:plexus-component-annotations:2.1.1")
implementation("javax.inject:javax.inject:1")

implementation("io.opentelemetry:opentelemetry-api")
implementation("io.opentelemetry:opentelemetry-sdk")
Expand All @@ -27,11 +27,11 @@ dependencies {
annotationProcessor("com.google.auto.value:auto-value")
compileOnly("com.google.auto.value:auto-value-annotations")

compileOnly("org.apache.maven:maven-core:3.5.0") // do not auto-update this version
compileOnly("org.apache.maven:maven-core:3.9.6") // do not auto-update this version
compileOnly("org.slf4j:slf4j-api")
compileOnly("org.sonatype.aether:aether-api:1.13.1")

testImplementation("org.apache.maven:maven-core:3.5.0")
testImplementation("org.apache.maven:maven-core:3.9.6")
testImplementation("org.slf4j:slf4j-simple")
}

Expand Down Expand Up @@ -60,3 +60,6 @@ tasks {
}

tasks.getByName("test").dependsOn("shadowJar")
repositories {
mavenCentral()
}
cyrille-leclerc marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,33 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import javax.annotation.PreDestroy;
import javax.inject.Named;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Service to configure the {@link OpenTelemetry} instance. */
@Component(role = OpenTelemetrySdkService.class, hint = "opentelemetry-service")
public final class OpenTelemetrySdkService implements Initializable, Disposable {
@Named
@Singleton
public final class OpenTelemetrySdkService {

static final String VERSION =
OpenTelemetrySdkService.class.getPackage().getImplementationVersion();

private static final Logger logger = LoggerFactory.getLogger(OpenTelemetrySdkService.class);

private OpenTelemetry openTelemetry = OpenTelemetry.noop();
@Nullable private OpenTelemetrySdk openTelemetrySdk;
private final OpenTelemetry openTelemetry;

@Nullable private Tracer tracer;
private final OpenTelemetrySdk openTelemetrySdk;

private boolean mojosInstrumentationEnabled;
private final Tracer tracer;

/** Visible for testing */
@Nullable AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk;
private final boolean mojosInstrumentationEnabled;

@Override
public synchronized void dispose() {
logger.debug("OpenTelemetry: dispose OpenTelemetrySdkService...");
OpenTelemetrySdk openTelemetrySdk = this.openTelemetrySdk;
if (openTelemetrySdk != null) {
logger.debug("OpenTelemetry: Shutdown SDK Trace Provider...");
CompletableResultCode sdkProviderShutdown =
openTelemetrySdk.getSdkTracerProvider().shutdown();
sdkProviderShutdown.join(10, TimeUnit.SECONDS);
if (sdkProviderShutdown.isSuccess()) {
logger.debug("OpenTelemetry: SDK Trace Provider shut down");
} else {
logger.warn(
"OpenTelemetry: Failure to shutdown SDK Trace Provider (done: {})",
sdkProviderShutdown.isDone());
}
this.openTelemetrySdk = null;
}
this.openTelemetry = OpenTelemetry.noop();

this.autoConfiguredOpenTelemetrySdk = null;
logger.debug("OpenTelemetry: OpenTelemetrySdkService disposed");
}
private boolean disposed;

@Override
public void initialize() {
public OpenTelemetrySdkService() {
logger.debug("OpenTelemetry: Initialize OpenTelemetrySdkService v{}...", VERSION);

// Change default of "otel.[traces,metrics,logs].exporter" from "otlp" to "none"
Expand All @@ -80,7 +56,7 @@ public void initialize() {
properties.put("otel.metrics.exporter", "none");
properties.put("otel.logs.exporter", "none");

this.autoConfiguredOpenTelemetrySdk =
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk =
AutoConfiguredOpenTelemetrySdk.builder()
.setServiceClassLoader(getClass().getClassLoader())
.addPropertiesSupplier(() -> properties)
Expand All @@ -94,11 +70,31 @@ public void initialize() {
this.openTelemetry = this.openTelemetrySdk;

Boolean mojoSpansEnabled = getBooleanConfig("otel.instrumentation.maven.mojo.enabled");
this.mojosInstrumentationEnabled = mojoSpansEnabled == null ? true : mojoSpansEnabled;
this.mojosInstrumentationEnabled = mojoSpansEnabled == null || mojoSpansEnabled;

this.tracer = openTelemetry.getTracer("io.opentelemetry.contrib.maven", VERSION);
}

@PreDestroy
public synchronized void preDestroy() {
logger.debug("OpenTelemetry: dispose OpenTelemetrySdkService...");
if (!disposed) {
logger.debug("OpenTelemetry: Shutdown SDK Trace Provider...");
CompletableResultCode sdkProviderShutdown =
this.openTelemetrySdk.getSdkTracerProvider().shutdown();
sdkProviderShutdown.join(10, TimeUnit.SECONDS);
if (sdkProviderShutdown.isSuccess()) {
logger.debug("OpenTelemetry: SDK Trace Provider shut down");
} else {
logger.warn(
"OpenTelemetry: Failure to shutdown SDK Trace Provider (done: {})",
sdkProviderShutdown.isDone());
}
this.disposed = true;
}
logger.debug("OpenTelemetry: OpenTelemetrySdkService disposed");
}

public Tracer getTracer() {
Tracer tracer = this.tracer;
if (tracer == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.maven.execution.AbstractExecutionListener;
import org.apache.maven.execution.ExecutionEvent;
Expand All @@ -29,20 +30,15 @@
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Close the OpenTelemetry SDK (see {@link OpenTelemetrySdkService#dispose()}) on the end of
* execution of the last project ({@link #projectSucceeded(ExecutionEvent)} and {@link
* #projectFailed(ExecutionEvent)}) rather than on the end of the Maven session {@link
* #sessionEnded(ExecutionEvent)} because OpenTelemetry and GRPC classes are unloaded by the Maven
* classloader before {@link #sessionEnded(ExecutionEvent)} causing {@link NoClassDefFoundError}
* messages in the logs.
* Don't mark this class as {@link javax.inject.Named} and {@link javax.inject.Singleton} because
* Maven Sisu doesn't automatically load instance of {@link ExecutionListener} as Maven Extension
* hooks the same way Maven Plexus did so we manually hook this instance of {@link
* ExecutionListener} through the {@link OtelLifecycleParticipant#afterProjectsRead(MavenSession)}.
*/
@Component(role = ExecutionListener.class, hint = "otel-execution-listener")
public final class OtelExecutionListener extends AbstractExecutionListener {

private static final Logger logger = LoggerFactory.getLogger(OtelExecutionListener.class);
Expand All @@ -56,17 +52,16 @@ public final class OtelExecutionListener extends AbstractExecutionListener {
*/
private static final ThreadLocal<Scope> MOJO_EXECUTION_SCOPE = new ThreadLocal<>();

@SuppressWarnings("NullAway") // Automatically initialized by DI
@Requirement
private SpanRegistry spanRegistry;
private final SpanRegistry spanRegistry;

@SuppressWarnings("NullAway") // Automatically initialized by DI
@Requirement
private OpenTelemetrySdkService openTelemetrySdkService;
private final OpenTelemetrySdkService openTelemetrySdkService;

private final Map<MavenGoal, MojoGoalExecutionHandler> mojoGoalExecutionHandlers;

public OtelExecutionListener() {
OtelExecutionListener(
SpanRegistry spanRegistry, OpenTelemetrySdkService openTelemetrySdkService) {
this.spanRegistry = spanRegistry;
this.openTelemetrySdkService = openTelemetrySdkService;
this.mojoGoalExecutionHandlers =
MojoGoalExecutionHandlerConfiguration.loadMojoGoalExecutionHandler(
OtelExecutionListener.class.getClassLoader());
Expand Down Expand Up @@ -102,36 +97,6 @@ public OtelExecutionListener() {
}
}

/**
* Register in given {@link OtelExecutionListener} to the lifecycle of the given {@link
* MavenSession}
*
* @see org.apache.maven.execution.MavenExecutionRequest#setExecutionListener(ExecutionListener)
*/
public static void registerOtelExecutionListener(
MavenSession session, OtelExecutionListener otelExecutionListener) {

ExecutionListener initialExecutionListener = session.getRequest().getExecutionListener();
if (initialExecutionListener instanceof ChainedExecutionListener
|| initialExecutionListener instanceof OtelExecutionListener) {
// already initialized
logger.debug(
"OpenTelemetry: OpenTelemetry extension already registered as execution listener, skip.");
} else if (initialExecutionListener == null) {
session.getRequest().setExecutionListener(otelExecutionListener);
logger.debug(
"OpenTelemetry: OpenTelemetry extension registered as execution listener. No execution listener initially defined");
} else {
session
.getRequest()
.setExecutionListener(
new ChainedExecutionListener(otelExecutionListener, initialExecutionListener));
logger.debug(
"OpenTelemetry: OpenTelemetry extension registered as execution listener. InitialExecutionListener: "
+ initialExecutionListener);
}
}

@Override
public void sessionStarted(ExecutionEvent executionEvent) {
MavenProject project = executionEvent.getSession().getTopLevelProject();
Expand Down Expand Up @@ -382,7 +347,7 @@ public Iterable<String> keys(Map<String, String> environmentVariables) {

@Override
@Nullable
public String get(@Nullable Map<String, String> environmentVariables, String key) {
public String get(@Nullable Map<String, String> environmentVariables, @Nonnull String key) {
return environmentVariables == null
? null
: environmentVariables.get(key.toUpperCase(Locale.ROOT));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,63 @@

package io.opentelemetry.maven;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.execution.ExecutionListener;
import org.apache.maven.execution.MavenSession;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Add the {@link OtelExecutionListener} to the lifecycle of the Maven execution */
@Component(role = AbstractMavenLifecycleParticipant.class)
@Named
@Singleton
public final class OtelLifecycleParticipant extends AbstractMavenLifecycleParticipant {

private static final Logger logger = LoggerFactory.getLogger(OtelLifecycleParticipant.class);

@SuppressWarnings("NullAway") // Automatically initialized by DI
@Requirement(role = ExecutionListener.class, hint = "otel-execution-listener")
private OtelExecutionListener otelExecutionListener;
private final OtelExecutionListener otelExecutionListener;

/**
* Manually instantiate {@link OtelExecutionListener} and hook it in the Maven build lifecycle
* because Maven Sisu doesn't load it when Maven Plexus did.
*/
@Inject
OtelLifecycleParticipant(
OpenTelemetrySdkService openTelemetrySdkService, SpanRegistry spanRegistry) {
this.otelExecutionListener = new OtelExecutionListener(spanRegistry, openTelemetrySdkService);
}

/**
* For an unknown reason, {@link #afterProjectsRead(MavenSession)} is invoked when the module is
* declared as an extension in pom.xml but {@link #afterSessionStart(MavenSession)} is not invoked
* declared as an extension in pom.xml but {@link #afterSessionStart(MavenSession)} is not
* invoked.
*/
@Override
public void afterProjectsRead(MavenSession session) {
OtelExecutionListener.registerOtelExecutionListener(session, this.otelExecutionListener);
ExecutionListener initialExecutionListener = session.getRequest().getExecutionListener();
if (initialExecutionListener instanceof ChainedExecutionListener
|| initialExecutionListener instanceof OtelExecutionListener) {
// already initialized
logger.debug(
"OpenTelemetry: OpenTelemetry extension already registered as execution listener, skip.");
} else if (initialExecutionListener == null) {
session.getRequest().setExecutionListener(this.otelExecutionListener);
logger.debug(
"OpenTelemetry: OpenTelemetry extension registered as execution listener. No execution listener initially defined");
} else {
session
.getRequest()
.setExecutionListener(
new ChainedExecutionListener(this.otelExecutionListener, initialExecutionListener));
logger.debug(
"OpenTelemetry: OpenTelemetry extension registered as execution listener. InitialExecutionListener: "
+ initialExecutionListener);
}
logger.debug("OpenTelemetry: afterProjectsRead");
}

@Override
public void afterSessionStart(MavenSession session) {
OtelExecutionListener.registerOtelExecutionListener(session, this.otelExecutionListener);
logger.debug("OpenTelemetry: afterSessionStart");
}

@Override
public void afterSessionEnd(MavenSession session) {
logger.debug("OpenTelemetry: afterSessionEnd");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -26,7 +27,8 @@
* Daemon</a>, can't execute multiple builds concurrently, there is no need to differentiate spans
* per {@link org.apache.maven.execution.MavenSession}.
*/
@Component(role = SpanRegistry.class)
@Singleton
@Named
public final class SpanRegistry {

private static final Logger logger = LoggerFactory.getLogger(SpanRegistry.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.Collections;
import java.util.List;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.project.MavenProject;
import org.slf4j.Logger;
Expand All @@ -36,7 +35,9 @@ public void enrichSpan(SpanBuilder spanBuilder, ExecutionEvent execution) {
spanBuilder.setSpanKind(SpanKind.CLIENT);

MavenProject project = execution.getProject();
ArtifactRepository optRepository = project.getDistributionManagementArtifactRepository();
@SuppressWarnings("deprecation") // there is no alternative to o.a.m.a.r.ArtifactRepository
org.apache.maven.artifact.repository.ArtifactRepository optRepository =
project.getDistributionManagementArtifactRepository();

if (optRepository == null) {
return;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<exportedPackage>io.opentelemetry.api.internal</exportedPackage>
<exportedPackage>io.opentelemetry.api.metrics</exportedPackage>
<exportedPackage>io.opentelemetry.api.trace</exportedPackage>
<exportedPackage>io.opentelemetry.context</exportedPackage>
<exportedPackage>io.opentelemetry.context</exportedPackage>
<exportedPackage>io.opentelemetry.context.propagation</exportedPackage>
cyrille-leclerc marked this conversation as resolved.
Show resolved Hide resolved
</exportedPackages>
</extension>
Loading
Loading