Skip to content

Commit 48ec2e1

Browse files
[maven-extension] Use the Otel SDK Auto Configuration Extension (#112)
* Use the Otel SDK Auto Configuration Extension * Fix typo: "coma" -> "comma" Co-authored-by: Anuraag Agrawal <[email protected]>
1 parent 0a694fd commit 48ec2e1

File tree

3 files changed

+87
-104
lines changed

3 files changed

+87
-104
lines changed

maven-extension/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ dependencies {
1717
implementation("io.opentelemetry:opentelemetry-api")
1818
implementation("io.opentelemetry:opentelemetry-sdk")
1919
implementation("io.opentelemetry:opentelemetry-sdk-trace")
20+
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
21+
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
2022
implementation("io.opentelemetry:opentelemetry-semconv")
2123
implementation("io.opentelemetry:opentelemetry-exporter-otlp")
2224
implementation("io.opentelemetry:opentelemetry-exporter-otlp-trace")

maven-extension/src/main/java/io/opentelemetry/maven/OpenTelemetrySdkService.java

Lines changed: 57 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,14 @@
66
package io.opentelemetry.maven;
77

88
import io.opentelemetry.api.OpenTelemetry;
9-
import io.opentelemetry.api.common.Attributes;
10-
import io.opentelemetry.api.common.AttributesBuilder;
119
import io.opentelemetry.api.trace.Tracer;
12-
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
1310
import io.opentelemetry.context.propagation.ContextPropagators;
14-
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
15-
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder;
1611
import io.opentelemetry.maven.semconv.MavenOtelSemanticAttributes;
1712
import io.opentelemetry.sdk.OpenTelemetrySdk;
13+
import io.opentelemetry.sdk.autoconfigure.OpenTelemetrySdkAutoConfiguration;
1814
import io.opentelemetry.sdk.common.CompletableResultCode;
19-
import io.opentelemetry.sdk.resources.Resource;
20-
import io.opentelemetry.sdk.trace.SdkTracerProvider;
21-
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
2215
import io.opentelemetry.sdk.trace.export.SpanExporter;
2316
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
24-
import java.time.Duration;
2517
import java.util.Map;
2618
import java.util.concurrent.TimeUnit;
2719
import org.apache.maven.rtinfo.RuntimeInformation;
@@ -36,17 +28,12 @@
3628
/**
3729
* Service to configure the {@link OpenTelemetry} instance.
3830
*
39-
* <p>Mimic the <a
40-
* href="https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure">OpenTelemetry
41-
* SDK Autoconfigure</a> that can't be used due to class loading issues when declaring the Maven
42-
* OpenTelemetry extension using the pom.xml {@code <extension>} declaration.
31+
* <p>Rely on the OpenTelemetry SDK AutoConfiguration extension. Parameters are passed as system
32+
* properties.
4333
*
44-
* <p>The OpenTelemetry SDK Autoconfigure extension registers a <a
45-
* href="https://github.com/open-telemetry/opentelemetry-java/blob/v1.6.0/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfiguration.java#L58">
46-
* JVM shutdown hook on {@code SdkTracerProvider#close()}</a> that is incompatible with the fact
47-
* that Maven extensions are unloaded before the JVM shuts down, requiring to close the trace
48-
* provider earlier in the lifecycle of the Maven build, and causing {@link NoClassDefFoundError}
49-
* when the shutdown hook is invoked.
34+
* <p>TODO: verify how we could use a composite {@link
35+
* io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties} combining the config passed by JVM
36+
* system properties and environment variables with overrides injected by the Otel Maven Extension
5037
*/
5138
@Component(role = OpenTelemetrySdkService.class, hint = "opentelemetry-service")
5239
public final class OpenTelemetrySdkService implements Initializable, Disposable {
@@ -55,7 +42,7 @@ public final class OpenTelemetrySdkService implements Initializable, Disposable
5542

5643
@Requirement private RuntimeInformation runtimeInformation;
5744

58-
private OpenTelemetry openTelemetry;
45+
private OpenTelemetry openTelemetry = OpenTelemetry.noop();
5946
private OpenTelemetrySdk openTelemetrySdk;
6047

6148
private Tracer tracer;
@@ -64,6 +51,18 @@ public final class OpenTelemetrySdkService implements Initializable, Disposable
6451

6552
private boolean mojosInstrumentationEnabled;
6653

54+
/**
55+
* Note: the JVM shutdown hook defined by the {@code
56+
* io.opentelemetry.sdk.autoconfigure.TracerProviderConfiguration} v1.7.0 does NOT cause
57+
* classloading issues even when Maven Plexus has unloaded the classes of the Otel Maven Extension
58+
* before the shutdown hook is invoked.
59+
*
60+
* <p>TODO create a feature request on {@code
61+
* io.opentelemetry.sdk.autoconfigure.TracerProviderConfiguration} to support the capability to
62+
* not register a JVM shutdown hook at initialization time (see
63+
* https://github.com/open-telemetry/opentelemetry-java/blob/v1.7.0/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfiguration.java#L58
64+
* )
65+
*/
6766
@Override
6867
public synchronized void dispose() {
6968
logger.debug("OpenTelemetry: dispose OpenTelemetrySdkService...");
@@ -87,78 +86,53 @@ public synchronized void dispose() {
8786
logger.debug("OpenTelemetry: OpenTelemetrySdkService disposed");
8887
}
8988

90-
/** TODO add support for `OTEL_EXPORTER_OTLP_CERTIFICATE` */
9189
@Override
9290
public void initialize() throws InitializationException {
9391
logger.debug("OpenTelemetry: initialize OpenTelemetrySdkService...");
94-
// OTEL_EXPORTER_OTLP_ENDPOINT
95-
String otlpEndpoint =
96-
System.getProperty(
97-
"otel.exporter.otlp.endpoint", System.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"));
98-
if (StringUtils.isBlank(otlpEndpoint)) {
92+
if (StringUtils.isBlank(
93+
OtelUtils.getSystemPropertyOrEnvironmentVariable(
94+
"otel.exporter.otlp.endpoint", "OTEL_EXPORTER_OTLP_ENDPOINT", null))) {
9995
logger.debug(
100-
"OpenTelemetry: No -Dotel.exporter.otlp.endpoint property or OTEL_EXPORTER_OTLP_ENDPOINT environment variable found, use a NOOP tracer");
101-
this.openTelemetry = OpenTelemetry.noop();
96+
"OpenTelemetry: No -Dotel.exporter.otlp.endpoint property or OTEL_EXPORTER_OTLP_ENDPOINT "
97+
+ "environment variable found, use a NOOP OpenTelemetry SDK");
10298
} else {
103-
// OtlpGrpcSpanExporterBuilder spanExporterBuilder = OtlpGrpcSpanExporter.builder();
104-
OtlpGrpcSpanExporterBuilder spanExporterBuilder = OtlpGrpcSpanExporter.builder();
105-
spanExporterBuilder.setEndpoint(otlpEndpoint);
106-
107-
// OTEL_EXPORTER_OTLP_HEADERS
108-
String otlpExporterHeadersAsString =
109-
System.getProperty(
110-
"otel.exporter.otlp.headers", System.getenv("OTEL_EXPORTER_OTLP_HEADERS"));
111-
Map<String, String> otlpExporterHeaders =
112-
OtelUtils.getCommaSeparatedMap(otlpExporterHeadersAsString);
113-
otlpExporterHeaders.forEach(spanExporterBuilder::addHeader);
114-
115-
// OTEL_EXPORTER_OTLP_TIMEOUT
116-
String otlpExporterTimeoutMillis =
117-
System.getProperty(
118-
"otel.exporter.otlp.timeout", System.getenv("OTEL_EXPORTER_OTLP_TIMEOUT"));
119-
if (StringUtils.isNotBlank(otlpExporterTimeoutMillis)) {
120-
try {
121-
spanExporterBuilder.setTimeout(
122-
Duration.ofMillis(Long.parseLong(otlpExporterTimeoutMillis)));
123-
} catch (NumberFormatException e) {
124-
logger.warn("OpenTelemetry: Skip invalid OTLP timeout " + otlpExporterTimeoutMillis, e);
99+
{
100+
// Don't use a {@code io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider} to inject
101+
// Maven runtime attributes due to a classloading issue when loading the Maven OpenTelemetry
102+
// extension as a pom.xml {@code <extension>}.
103+
String initialCommaSeparatedAttributes =
104+
OtelUtils.getSystemPropertyOrEnvironmentVariable(
105+
"otel.resource.attributes", "OTEL_RESOURCE_ATTRIBUTES", "");
106+
Map<String, String> attributes =
107+
OtelUtils.getCommaSeparatedMap(initialCommaSeparatedAttributes);
108+
109+
// service.name
110+
String serviceName =
111+
OtelUtils.getSystemPropertyOrEnvironmentVariable(
112+
"otel.service.name", "OTEL_SERVICE_NAME", null);
113+
114+
if (!attributes.containsKey(ResourceAttributes.SERVICE_NAME.getKey())
115+
&& StringUtils.isBlank(serviceName)) {
116+
// service.name is not defined in passed configuration, we define it
117+
attributes.put(
118+
ResourceAttributes.SERVICE_NAME.getKey(),
119+
MavenOtelSemanticAttributes.ServiceNameValues.SERVICE_NAME_VALUE);
120+
}
121+
122+
// service.version
123+
final String mavenVersion = this.runtimeInformation.getMavenVersion();
124+
if (!attributes.containsKey(ResourceAttributes.SERVICE_VERSION.getKey())) {
125+
attributes.put(ResourceAttributes.SERVICE_VERSION.getKey(), mavenVersion);
125126
}
126-
}
127127

128-
this.spanExporter = spanExporterBuilder.build();
129-
130-
// OTEL_RESOURCE_ATTRIBUTES
131-
AttributesBuilder resourceAttributesBuilder = Attributes.builder();
132-
Resource mavenResource = getMavenResource();
133-
resourceAttributesBuilder.putAll(mavenResource.getAttributes());
134-
String otelResourceAttributesAsString =
135-
System.getProperty("otel.resource.attributes", System.getenv("OTEL_RESOURCE_ATTRIBUTES"));
136-
if (StringUtils.isNotBlank(otelResourceAttributesAsString)) {
137-
Map<String, String> otelResourceAttributes =
138-
OtelUtils.getCommaSeparatedMap(otelResourceAttributesAsString);
139-
// see io.opentelemetry.sdk.autoconfigure.EnvironmentResource.getAttributes
140-
otelResourceAttributes.forEach(resourceAttributesBuilder::put);
128+
String newCommaSeparatedAttributes = OtelUtils.getCommaSeparatedString(attributes);
129+
logger.debug(
130+
"OpenTelemetry: Initial resource attributes: {}", initialCommaSeparatedAttributes);
131+
logger.debug("OpenTelemetry: Use resource attributes: {}", newCommaSeparatedAttributes);
132+
System.setProperty("otel.resource.attributes", newCommaSeparatedAttributes);
141133
}
142-
final Attributes resourceAttributes = resourceAttributesBuilder.build();
143134

144-
logger.debug(
145-
"OpenTelemetry: Export OpenTelemetry traces to {} with attributes: {}",
146-
otlpEndpoint,
147-
resourceAttributes);
148-
149-
final BatchSpanProcessor batchSpanProcessor =
150-
BatchSpanProcessor.builder(spanExporter).build();
151-
SdkTracerProvider sdkTracerProvider =
152-
SdkTracerProvider.builder()
153-
.setResource(Resource.create(resourceAttributes))
154-
.addSpanProcessor(batchSpanProcessor)
155-
.build();
156-
157-
this.openTelemetrySdk =
158-
OpenTelemetrySdk.builder()
159-
.setTracerProvider(sdkTracerProvider)
160-
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
161-
.build();
135+
this.openTelemetrySdk = OpenTelemetrySdkAutoConfiguration.initialize(false);
162136
this.openTelemetry = this.openTelemetrySdk;
163137
}
164138

@@ -180,21 +154,6 @@ public Tracer getTracer() {
180154
return tracer;
181155
}
182156

183-
/**
184-
* Don't use a {@code io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider} due to classloading
185-
* issue when loading the Maven OpenTelemetry extension as a pom.xml {@code <extension>}.
186-
*/
187-
protected Resource getMavenResource() {
188-
final String mavenVersion = this.runtimeInformation.getMavenVersion();
189-
final Attributes attributes =
190-
Attributes.of(
191-
ResourceAttributes.SERVICE_NAME,
192-
MavenOtelSemanticAttributes.ServiceNameValues.SERVICE_NAME_VALUE,
193-
ResourceAttributes.SERVICE_VERSION,
194-
mavenVersion);
195-
return Resource.create(attributes);
196-
}
197-
198157
/** Returns the {@link ContextPropagators} for this {@link OpenTelemetry}. */
199158
public ContextPropagators getPropagators() {
200159
return openTelemetry.getPropagators();

maven-extension/src/main/java/io/opentelemetry/maven/OtelUtils.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,33 @@
77

88
import java.util.AbstractMap;
99
import java.util.Arrays;
10-
import java.util.Collections;
10+
import java.util.HashMap;
1111
import java.util.LinkedHashMap;
1212
import java.util.List;
1313
import java.util.Map;
1414
import java.util.stream.Collectors;
15+
import javax.annotation.CheckForNull;
16+
import javax.annotation.Nullable;
1517

1618
final class OtelUtils {
1719

18-
public static Map<String, String> getCommaSeparatedMap(String comaSeparatedKeyValuePairs) {
19-
if (StringUtils.isBlank(comaSeparatedKeyValuePairs)) {
20-
return Collections.emptyMap();
20+
public static String getCommaSeparatedString(Map<String, String> keyValuePairs) {
21+
return keyValuePairs.entrySet().stream()
22+
.map(keyValuePair -> keyValuePair.getKey() + "=" + keyValuePair.getValue())
23+
.collect(Collectors.joining(","));
24+
}
25+
26+
public static Map<String, String> getCommaSeparatedMap(String commaSeparatedKeyValuePairs) {
27+
if (StringUtils.isBlank(commaSeparatedKeyValuePairs)) {
28+
return new HashMap<>();
2129
}
22-
return filterBlanksAndNulls(comaSeparatedKeyValuePairs.split(",")).stream()
30+
return filterBlanksAndNulls(commaSeparatedKeyValuePairs.split(",")).stream()
2331
.map(keyValuePair -> filterBlanksAndNulls(keyValuePair.split("=", 2)))
2432
.map(
2533
splitKeyValuePairs -> {
2634
if (splitKeyValuePairs.size() != 2) {
2735
throw new RuntimeException(
28-
"Invalid key-value paire: " + comaSeparatedKeyValuePairs);
36+
"Invalid key-value pair: " + commaSeparatedKeyValuePairs);
2937
}
3038
return new AbstractMap.SimpleImmutableEntry<>(
3139
splitKeyValuePairs.get(0), splitKeyValuePairs.get(1));
@@ -43,4 +51,18 @@ private static List<String> filterBlanksAndNulls(String[] values) {
4351
.filter(s -> !s.isEmpty())
4452
.collect(Collectors.toList());
4553
}
54+
55+
@CheckForNull
56+
public static String getSystemPropertyOrEnvironmentVariable(
57+
String systemPropertyName, String environmentVariableName, @Nullable String defaultValue) {
58+
String systemProperty = System.getProperty(systemPropertyName);
59+
if (StringUtils.isNotBlank(systemProperty)) {
60+
return systemProperty;
61+
}
62+
String environmentVariable = System.getenv(environmentVariableName);
63+
if (StringUtils.isNotBlank(environmentVariable)) {
64+
return environmentVariable;
65+
}
66+
return defaultValue;
67+
}
4668
}

0 commit comments

Comments
 (0)