Skip to content

Commit b72d168

Browse files
Maven OpenTelemetry Extension initial commit (#79)
1 parent ebf5dd1 commit b72d168

17 files changed

+1172
-0
lines changed

maven-extension/README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Maven OpenTelemetry extension
2+
3+
Maven extension to observe Maven builds as distributed traces.
4+
5+
## Getting Started
6+
7+
The Maven OpenTelemetry Extension is configured using environment variables or JVM system properties and can be added to a build using one of the following ways:
8+
* adding the extension jar to `${maven.home}/lib/ext`
9+
* adding the path to the extension jar to`-Dmaven.ext.class.path`,
10+
* adding the extension as a build extension in the `pom.xml`,
11+
* (since Maven 3.3.1) configuring the extension in `.mvn/extensions.xml`.
12+
13+
14+
### Adding the extension to the classpath
15+
16+
Add the Maven OpenTelemetry Extension to `${maven.home}/lib/ext` or to the classpath using `-Dmaven.ext.class.path=`.
17+
18+
```
19+
cp /path/to/opentelemetry-java-contrib/maven-extension/build/libs/opentelemetry-maven-extension-1.6.0-SNAPSHOT.jar target/dependency/opentelemetry-maven-extension.jar
20+
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
21+
22+
mvn -Dmaven.ext.class.path=target/dependency/opentelemetry-maven-extension.jar verify
23+
```
24+
25+
### Declaring the extension in the `pom.xml` file
26+
27+
WARNING documentation is invalid until the first release of the io.opentelemetry.contrib:opentelemetry-maven-extension is published on a public Maven repository
28+
29+
Add the Maven OpenTelemetry Extension in the `pom.xml` file:
30+
31+
```xml
32+
<project>
33+
...
34+
<build>
35+
<extensions>
36+
<extension>
37+
<groupId>io.opentelemetry.contrib</groupId>
38+
<artifactId>opentelemetry-maven-extension</artifactId>
39+
<version>1.6.0-SNAPSHOT</version>
40+
</extension>
41+
</extensions>
42+
</build>
43+
</project>
44+
```
45+
46+
```
47+
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
48+
49+
mvn verify
50+
```
51+
52+
## Configuration
53+
54+
The Maven OpenTelemetry Extension supports a subset of the [OpenTelemetry auto configuration environment variables and JVM system properties](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure).
55+
56+
| System property | Environment variable | Description |
57+
|------------------------------|-----------------------------|---------------------------------------------------------------------------|
58+
| otel.exporter.otlp.endpoint | OTEL_EXPORTER_OTLP_ENDPOINT | The OTLP traces and metrics endpoint to connect to. Must be a URL with a scheme of either `http` or `https` based on the use of TLS. Example `http://localhost:4317`. |
59+
| otel.exporter.otlp.headers | OTEL_EXPORTER_OTLP_HEADERS | Key-value pairs separated by commas to pass as request headers on OTLP trace and metrics requests. |
60+
| otel.exporter.otlp.timeout | OTEL_EXPORTER_OTLP_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP trace and metric batch. Default is `10000`. |
61+
| otel.resource.attributes | OTEL_RESOURCE_ATTRIBUTES | Specify resource attributes in the following format: key1=val1,key2=val2,key3=val3 |
62+
63+
64+
ℹ️ The `service.name` is set by default to `maven`, it can be overwritten specifying resource atributes.
65+
66+
67+
## Examples
68+
69+
Example of a trace of a Maven build.
70+
71+
![](https://raw.githubusercontent.com/cyrille-leclerc/opentelemetry-java-contrib/add-maven-extension-v2/maven-extension/docs/images/maven-execution-trace-jaeger.png)
72+
73+
## Example of a distributed trace of a Jenkins pipeline executing a Maven build
74+
75+
Distributed trace of a Jenkins pipeline invoking a Maven build instrumented with the [Jenkins OpenTelemetry plugin](https://plugins.jenkins.io/opentelemetry/) and the OpenTelemetry Maven Extension and visualized with [Jaeger Tracing](https://www.jaegertracing.io/)
76+
77+
![](https://raw.githubusercontent.com/cyrille-leclerc/opentelemetry-java-contrib/add-maven-extension-v2/maven-extension/docs/images/jenkins-maven-execution-trace-jaeger.png)
78+
79+
# Other CI/CD Tools supporting OpenTelemetry traces
80+
81+
List of other CI/CD tools that support OpenTelemetry traces and integrate with the Maven OpenTelemetry Extension creating a distributed traces providing end to end visibility.
82+
83+
## Jenkins OpenTelemetry Plugin
84+
85+
The [Jenkins OpenTelemetry Plugin](https://plugins.jenkins.io/opentelemetry/) exposes Jenkins pipelines & jobs as OpenTelemetry traces and exposes Jenkins health indicators as OpenTelemetry metrics.
86+
87+
## Otel CLI
88+
89+
The [`otel-cli`](https://github.com/equinix-labs/otel-cli) is a command line wrapper to observe the execution of a shell command as an OpenTelemetry trace.

maven-extension/build.gradle.kts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
plugins {
2+
id("java")
3+
id("com.github.johnrengelman.shadow")
4+
id("otel.java-conventions")
5+
id("otel.publish-conventions")
6+
}
7+
8+
// NOTE
9+
// `META-INF/plexus/components.xml` is manually handled under src/main/resources because there is no Gradle
10+
// equivalent to the Maven plugin `plexus-component-metadata:generate-metadata`
11+
12+
description = "Maven extension to observe Maven builds with distributed traces using OpenTelemetry SDK"
13+
14+
dependencies {
15+
implementation("org.codehaus.plexus:plexus-component-annotations:2.1.0")
16+
17+
implementation("io.opentelemetry:opentelemetry-api")
18+
implementation("io.opentelemetry:opentelemetry-sdk")
19+
implementation("io.opentelemetry:opentelemetry-sdk-trace")
20+
implementation("io.opentelemetry:opentelemetry-semconv")
21+
implementation("io.opentelemetry:opentelemetry-exporter-otlp")
22+
implementation("io.opentelemetry:opentelemetry-exporter-otlp-trace")
23+
24+
implementation("io.grpc:grpc-netty-shaded")
25+
26+
annotationProcessor("com.google.auto.value:auto-value")
27+
compileOnly("com.google.auto.value:auto-value-annotations")
28+
29+
compileOnly("org.apache.maven:maven-core:3.5.0")
30+
compileOnly("org.slf4j:slf4j-api")
31+
compileOnly("org.sonatype.aether:aether-api:1.13.1")
32+
33+
34+
testImplementation("org.apache.maven:maven-core:3.5.0")
35+
testImplementation("org.slf4j:slf4j-simple")
36+
}
37+
38+
tasks {
39+
shadowJar {
40+
archiveClassifier.set("")
41+
}
42+
43+
assemble {
44+
dependsOn(shadowJar)
45+
}
46+
}
47+
267 KB
Loading
167 KB
Loading
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package io.opentelemetry.maven;
6+
7+
import java.util.Arrays;
8+
import java.util.List;
9+
import java.util.stream.Collectors;
10+
import org.apache.maven.execution.ExecutionEvent;
11+
import org.apache.maven.execution.ExecutionListener;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
15+
/**
16+
* Util class to chain multiple {@link ExecutionListener} as Maven APIs don't offer this capability.
17+
*/
18+
final class ChainedExecutionListener implements ExecutionListener {
19+
private final Logger logger = LoggerFactory.getLogger(getClass());
20+
21+
private final List<ExecutionListener> listeners;
22+
23+
/** @param listeners {@code null} values are filtered */
24+
ChainedExecutionListener(ExecutionListener... listeners) {
25+
this.listeners = Arrays.stream(listeners).filter(e -> e != null).collect(Collectors.toList());
26+
}
27+
28+
@Override
29+
public void projectDiscoveryStarted(ExecutionEvent event) {
30+
for (ExecutionListener listener : this.listeners) {
31+
listener.projectDiscoveryStarted(event);
32+
}
33+
}
34+
35+
@Override
36+
public void sessionStarted(ExecutionEvent event) {
37+
for (ExecutionListener listener : this.listeners) {
38+
listener.sessionStarted(event);
39+
}
40+
}
41+
42+
@Override
43+
public void sessionEnded(ExecutionEvent event) {
44+
for (ExecutionListener listener : this.listeners) {
45+
listener.sessionEnded(event);
46+
}
47+
}
48+
49+
@Override
50+
public void projectSkipped(ExecutionEvent event) {
51+
for (ExecutionListener listener : this.listeners) {
52+
listener.projectSkipped(event);
53+
}
54+
}
55+
56+
@Override
57+
public void projectStarted(ExecutionEvent event) {
58+
for (ExecutionListener listener : this.listeners) {
59+
listener.projectStarted(event);
60+
}
61+
}
62+
63+
@Override
64+
public void projectSucceeded(ExecutionEvent event) {
65+
for (ExecutionListener listener : this.listeners) {
66+
listener.projectSucceeded(event);
67+
}
68+
}
69+
70+
@Override
71+
public void projectFailed(ExecutionEvent event) {
72+
for (ExecutionListener listener : this.listeners) {
73+
listener.projectFailed(event);
74+
}
75+
}
76+
77+
@Override
78+
public void mojoSkipped(ExecutionEvent event) {
79+
for (ExecutionListener listener : this.listeners) {
80+
listener.mojoSkipped(event);
81+
}
82+
}
83+
84+
@Override
85+
public void mojoStarted(ExecutionEvent event) {
86+
for (ExecutionListener listener : this.listeners) {
87+
listener.mojoStarted(event);
88+
}
89+
}
90+
91+
@Override
92+
public void mojoSucceeded(ExecutionEvent event) {
93+
for (ExecutionListener listener : this.listeners) {
94+
listener.mojoSucceeded(event);
95+
}
96+
}
97+
98+
@Override
99+
public void mojoFailed(ExecutionEvent event) {
100+
for (ExecutionListener listener : this.listeners) {
101+
listener.mojoFailed(event);
102+
}
103+
}
104+
105+
@Override
106+
public void forkStarted(ExecutionEvent event) {
107+
for (ExecutionListener listener : this.listeners) {
108+
listener.forkStarted(event);
109+
}
110+
}
111+
112+
@Override
113+
public void forkSucceeded(ExecutionEvent event) {
114+
for (ExecutionListener listener : this.listeners) {
115+
listener.forkSucceeded(event);
116+
}
117+
}
118+
119+
@Override
120+
public void forkFailed(ExecutionEvent event) {
121+
for (ExecutionListener listener : this.listeners) {
122+
listener.forkFailed(event);
123+
}
124+
}
125+
126+
@Override
127+
public void forkedProjectStarted(ExecutionEvent event) {
128+
for (ExecutionListener listener : this.listeners) {
129+
listener.forkedProjectStarted(event);
130+
}
131+
}
132+
133+
@Override
134+
public void forkedProjectSucceeded(ExecutionEvent event) {
135+
for (ExecutionListener listener : this.listeners) {
136+
listener.forkedProjectSucceeded(event);
137+
}
138+
}
139+
140+
@Override
141+
public void forkedProjectFailed(ExecutionEvent event) {
142+
for (ExecutionListener listener : this.listeners) {
143+
listener.forkedProjectFailed(event);
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)