Skip to content

Commit 87615cc

Browse files
authored
add support for OTEL_RESOURCE_ATTRIBUTES, OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_HEADERS and OTEL_EXPORTER_OTLP_PROTOCOL for spring boot starter (#9950)
1 parent 67facf3 commit 87615cc

30 files changed

+772
-193
lines changed

instrumentation/spring/spring-boot-autoconfigure/README.md

+79-25
Original file line numberDiff line numberDiff line change
@@ -390,51 +390,105 @@ If an exporter is present in the classpath during runtime and a spring bean of t
390390

391391
#### Configuration Properties
392392

393-
##### Enabling/Disabling Features
394-
395-
| Feature | Property | Default Value | ConditionalOnClass |
396-
|------------------|---------------------------------------------|---------------|------------------------|
397-
| spring-web | otel.instrumentation.spring-webmvc.enabled | `true` | RestTemplate |
398-
| spring-webmvc | otel.instrumentation.spring-web.enabled | `true` | OncePerRequestFilter |
399-
| spring-webflux | otel.instrumentation.spring-webflux.enabled | `true` | WebClient |
400-
| @WithSpan | otel.instrumentation.annotations.enabled | `true` | WithSpan, Aspect |
401-
| Otlp Exporter | otel.exporter.otlp.enabled | `true` | OtlpGrpcSpanExporter |
402-
| Jaeger Exporter | otel.exporter.jaeger.enabled | `true` | JaegerGrpcSpanExporter |
403-
| Zipkin Exporter | otel.exporter.zipkin.enabled | `true` | ZipkinSpanExporter |
404-
| Logging Exporter | otel.exporter.logging.enabled | `true` | LoggingSpanExporter |
393+
##### Enabling/Disabling Exporters
394+
395+
All exporters can be enabled or disabled as in the
396+
[SDK auto-configuration](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#exporters).
397+
This is the preferred way to enable/disable exporters and takes precedence over the properties below.
398+
399+
| Feature | Property | Default Value | ConditionalOnClass |
400+
|-----------------------|---------------------------------------------|---------------|---------------------------|
401+
| Otlp Exporter | otel.exporter.otlp.enabled | `true` | - |
402+
| Otlp Span Exporter | otel.exporter.otlp.traces.enabled | `true` | OtlpGrpcSpanExporter |
403+
| Otlp Metrics Exporter | otel.exporter.otlp.metrics.enabled | `true` | OtlpGrpcMetricExporter |
404+
| Otlp Logs Exporter | otel.exporter.otlp.logs.enabled | `true` | OtlpGrpcLogRecordExporter |
405+
| Jaeger Exporter | otel.exporter.jaeger.enabled | `true` | JaegerGrpcSpanExporter |
406+
| Zipkin Exporter | otel.exporter.zipkin.enabled | `true` | ZipkinSpanExporter |
407+
| Logging Exporter | otel.exporter.logging.enabled | `false` | LoggingSpanExporter |
405408

406409
<!-- Slf4j Log Correlation otel.springboot.loggers.slf4j.enabled true org.slf4j.MDC -->
407410

408-
##### Resource Properties
411+
##### Enabling/Disabling Features
409412

410-
| Feature | Property | Default Value |
411-
| -------- | ------------------------------------------------ | ---------------------- |
412-
| Resource | otel.springboot.resource.enabled | `true` |
413-
| | otel.springboot.resource.attributes.service.name | `unknown_service:java` |
414-
| | otel.springboot.resource.attributes | `empty map` |
413+
| Feature | Property | Default Value | ConditionalOnClass |
414+
|-----------------------|---------------------------------------------|---------------|---------------------------|
415+
| spring-web | otel.instrumentation.spring-webmvc.enabled | `true` | RestTemplate |
416+
| spring-webmvc | otel.instrumentation.spring-web.enabled | `true` | OncePerRequestFilter |
417+
| spring-webflux | otel.instrumentation.spring-webflux.enabled | `true` | WebClient |
418+
| @WithSpan | otel.instrumentation.annotations.enabled | `true` | WithSpan, Aspect |
415419

416-
`unknown_service:java` will be used as the service-name if no value has been specified to the
417-
property `spring.application.name` or `otel.springboot.resource.attributes.service.name` (which has
418-
the highest priority)
420+
##### Resource Properties
421+
422+
| Feature | Property | Default Value |
423+
| -------- |---------------------------------------------------------------------| ---------------------- |
424+
| Resource | otel.springboot.resource.enabled | `true` |
425+
| | otel.resource.attributes (old: otel.springboot.resource.attributes) | `empty map` |
419426

420-
`otel.springboot.resource.attributes` supports a pattern-based resource configuration in the
427+
`otel.resource.attributes` supports a pattern-based resource configuration in the
421428
application.properties like this:
422429

423430
```
424-
otel.springboot.resource.attributes.environment=dev
425-
otel.springboot.resource.attributes.xyz=foo
431+
otel.resource.attributes.environment=dev
432+
otel.resource.attributes.xyz=foo
433+
```
434+
435+
It's also possible to specify the resource attributes in `application.yaml`:
436+
437+
```yaml
438+
otel:
439+
resource:
440+
attributes:
441+
environment: dev
442+
xyz: foo
443+
```
444+
445+
Finally, the resource attributes can be specified as a comma-separated list, as described in the
446+
[specification](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_resource_attributes):
447+
448+
```shell
449+
export OTEL_RESOURCE_ATTRIBUTES="key1=value1,key2=value2"
426450
```
427451

452+
The service name is determined by the following precedence, in accordance with the OpenTelemetry
453+
[specification](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_service_name):
454+
455+
1. `otel.service.name` spring property or `OTEL_SERVICE_NAME` environment variable (highest
456+
precedence)
457+
2. `service.name` in `otel.resource.attributes` system/spring property or `OTEL_RESOURCE_ATTRIBUTES`
458+
environment variable
459+
3. `service.name` in `otel.springboot.resource.attributes` system/spring property
460+
4. `spring.application.name` spring property
461+
5. the default value `unknown_service:java` (lowest precedence)
462+
428463
##### Exporter Properties
429464

430465
| Feature | Property | Default Value |
431-
| --------------- | ----------------------------- | ------------------------------------ |
466+
|-----------------|-------------------------------|--------------------------------------|
432467
| Otlp Exporter | otel.exporter.otlp.endpoint | `localhost:4317` |
468+
| | otel.exporter.otlp.protocol | `grpc` |
469+
| | otel.exporter.otlp.headers | |
433470
| | otel.exporter.otlp.timeout | `1s` |
434471
| Jaeger Exporter | otel.exporter.jaeger.endpoint | `localhost:14250` |
435472
| | otel.exporter.jaeger.timeout | `1s` |
436473
| Zipkin Exporter | otel.exporter.jaeger.endpoint | `http://localhost:9411/api/v2/spans` |
437474

475+
The `otel.exporter.otlp.headers` property can be specified as a comma-separated list,
476+
which is compliant with the
477+
[specification](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_headers).
478+
Similar to the resource attributes, the headers can be specified in `application.properties` or
479+
`application.yaml`:
480+
481+
```yaml
482+
otel:
483+
exporter:
484+
otlp:
485+
headers:
486+
- key: "header1"
487+
value: "value1"
488+
- key: "header2"
489+
value: "value2"
490+
```
491+
438492
##### Tracer Properties
439493
440494
| Feature | Property | Default Value |

instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ testing {
9090
implementation(project(":testing-common"))
9191
implementation("io.opentelemetry:opentelemetry-sdk")
9292
implementation("io.opentelemetry:opentelemetry-sdk-testing")
93+
implementation("org.mockito:mockito-inline")
9394
implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
9495

9596
implementation(project(":instrumentation:logback:logback-appender-1.0:library"))

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java

+20
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
import io.opentelemetry.api.OpenTelemetry;
99
import io.opentelemetry.api.trace.TracerProvider;
1010
import io.opentelemetry.context.propagation.ContextPropagators;
11+
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpLoggerExporterAutoConfiguration;
12+
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration;
13+
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration;
14+
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.MapConverter;
15+
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration;
1116
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringResourceConfigProperties;
1217
import io.opentelemetry.sdk.OpenTelemetrySdk;
1318
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
@@ -30,8 +35,10 @@
3035
import java.util.Collections;
3136
import java.util.List;
3237
import org.springframework.beans.factory.ObjectProvider;
38+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3339
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3440
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
41+
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
3542
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3643
import org.springframework.context.annotation.Bean;
3744
import org.springframework.context.annotation.Configuration;
@@ -56,6 +63,19 @@ public OpenTelemetryAutoConfiguration() {}
5663
@ConditionalOnProperty(name = "otel.sdk.disabled", havingValue = "false", matchIfMissing = true)
5764
public static class OpenTelemetrySdkConfig {
5865

66+
@Bean
67+
@ConfigurationPropertiesBinding
68+
@ConditionalOnBean({
69+
OtelResourceAutoConfiguration.class,
70+
OtlpLoggerExporterAutoConfiguration.class,
71+
OtlpSpanExporterAutoConfiguration.class,
72+
OtlpMetricExporterAutoConfiguration.class
73+
})
74+
public MapConverter mapConverter() {
75+
// needed for otlp exporter headers and OtelResourceProperties
76+
return new MapConverter();
77+
}
78+
5979
@Bean
6080
@ConditionalOnMissingBean
6181
public SdkTracerProvider sdkTracerProvider(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal;
7+
8+
import java.util.Arrays;
9+
import javax.annotation.Nullable;
10+
import org.springframework.core.env.Environment;
11+
12+
/**
13+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
14+
* any time.
15+
*/
16+
public final class ExporterConfigEvaluator {
17+
18+
private ExporterConfigEvaluator() {}
19+
20+
public static boolean isExporterEnabled(
21+
Environment environment,
22+
@Nullable String oldAllKey,
23+
String oldKey,
24+
String exportersKey,
25+
String wantExporter,
26+
boolean defaultValue) {
27+
28+
String exporter = environment.getProperty(exportersKey);
29+
if (exporter != null) {
30+
return Arrays.asList(exporter.split(",")).contains(wantExporter);
31+
}
32+
33+
String old = environment.getProperty(oldKey);
34+
if (old != null) {
35+
return "true".equals(old);
36+
}
37+
if (oldAllKey != null) {
38+
String oldAll = environment.getProperty(oldAllKey);
39+
if (oldAll != null) {
40+
return "true".equals(oldAll);
41+
}
42+
}
43+
return defaultValue;
44+
}
45+
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/jaeger/JaegerSpanExporterAutoConfiguration.java

+19-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.jaeger;
77

88
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
9+
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.ExporterConfigEvaluator;
910
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
1011
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
1112
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
12-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
1313
import org.springframework.boot.context.properties.EnableConfigurationProperties;
1414
import org.springframework.context.annotation.Bean;
15+
import org.springframework.context.annotation.Condition;
16+
import org.springframework.context.annotation.Conditional;
1517
import org.springframework.context.annotation.Configuration;
1618

1719
/**
@@ -23,7 +25,7 @@
2325
@Configuration
2426
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
2527
@EnableConfigurationProperties(JaegerSpanExporterProperties.class)
26-
@ConditionalOnProperty(prefix = "otel.exporter.jaeger", name = "enabled", matchIfMissing = true)
28+
@Conditional(JaegerSpanExporterAutoConfiguration.CustomCondition.class)
2729
@ConditionalOnClass(io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter.class)
2830
@Deprecated
2931
public class JaegerSpanExporterAutoConfiguration {
@@ -43,4 +45,19 @@ public io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter otelJaegerSpanExp
4345
}
4446
return builder.build();
4547
}
48+
49+
static final class CustomCondition implements Condition {
50+
@Override
51+
public boolean matches(
52+
org.springframework.context.annotation.ConditionContext context,
53+
org.springframework.core.type.AnnotatedTypeMetadata metadata) {
54+
return ExporterConfigEvaluator.isExporterEnabled(
55+
context.getEnvironment(),
56+
null,
57+
"otel.exporter.jaeger.enabled",
58+
"otel.traces.exporter",
59+
"jaeger",
60+
true);
61+
}
62+
}
4663
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfiguration.java

+15-13
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@
88
import io.opentelemetry.exporter.logging.LoggingMetricExporter;
99
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
1010
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
11+
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.ExporterConfigEvaluator;
1112
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
12-
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
1313
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
14-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
1514
import org.springframework.boot.context.properties.EnableConfigurationProperties;
1615
import org.springframework.context.annotation.Bean;
16+
import org.springframework.context.annotation.Condition;
1717
import org.springframework.context.annotation.Conditional;
1818
import org.springframework.context.annotation.Configuration;
1919

2020
/** Configures {@link LoggingSpanExporter} bean for tracing. */
2121
@Configuration
2222
@EnableConfigurationProperties(LoggingExporterProperties.class)
2323
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
24-
@Conditional(LoggingMetricExporterAutoConfiguration.AnyPropertyEnabled.class)
24+
@Conditional(LoggingMetricExporterAutoConfiguration.CustomCondition.class)
2525
@ConditionalOnClass(LoggingMetricExporter.class)
2626
public class LoggingMetricExporterAutoConfiguration {
2727

@@ -30,16 +30,18 @@ public LoggingMetricExporter otelLoggingMetricExporter() {
3030
return LoggingMetricExporter.create();
3131
}
3232

33-
static final class AnyPropertyEnabled extends AnyNestedCondition {
34-
35-
AnyPropertyEnabled() {
36-
super(ConfigurationPhase.PARSE_CONFIGURATION);
33+
static final class CustomCondition implements Condition {
34+
@Override
35+
public boolean matches(
36+
org.springframework.context.annotation.ConditionContext context,
37+
org.springframework.core.type.AnnotatedTypeMetadata metadata) {
38+
return ExporterConfigEvaluator.isExporterEnabled(
39+
context.getEnvironment(),
40+
"otel.exporter.logging.enabled",
41+
"otel.exporter.logging.metrics.enabled",
42+
"otel.metrics.exporter",
43+
"logging",
44+
false);
3745
}
38-
39-
@ConditionalOnProperty("otel.exporter.logging.enabled")
40-
static class LoggingEnabled {}
41-
42-
@ConditionalOnProperty("otel.exporter.logging.metrics.enabled")
43-
static class LoggingMetricsEnabled {}
4446
}
4547
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfiguration.java

+15-13
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77

88
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
99
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
10+
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.ExporterConfigEvaluator;
1011
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
11-
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
1212
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
1313
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
14-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
1514
import org.springframework.boot.context.properties.EnableConfigurationProperties;
1615
import org.springframework.context.annotation.Bean;
16+
import org.springframework.context.annotation.Condition;
1717
import org.springframework.context.annotation.Conditional;
1818
import org.springframework.context.annotation.Configuration;
1919

2020
/** Configures {@link LoggingSpanExporter} bean for tracing. */
2121
@Configuration
2222
@EnableConfigurationProperties(LoggingExporterProperties.class)
2323
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
24-
@Conditional(LoggingSpanExporterAutoConfiguration.AnyPropertyEnabled.class)
24+
@Conditional(LoggingSpanExporterAutoConfiguration.CustomCondition.class)
2525
@ConditionalOnClass(LoggingSpanExporter.class)
2626
public class LoggingSpanExporterAutoConfiguration {
2727

@@ -31,16 +31,18 @@ public LoggingSpanExporter otelLoggingSpanExporter() {
3131
return LoggingSpanExporter.create();
3232
}
3333

34-
static final class AnyPropertyEnabled extends AnyNestedCondition {
35-
36-
AnyPropertyEnabled() {
37-
super(ConfigurationPhase.PARSE_CONFIGURATION);
34+
static final class CustomCondition implements Condition {
35+
@Override
36+
public boolean matches(
37+
org.springframework.context.annotation.ConditionContext context,
38+
org.springframework.core.type.AnnotatedTypeMetadata metadata) {
39+
return ExporterConfigEvaluator.isExporterEnabled(
40+
context.getEnvironment(),
41+
"otel.exporter.logging.enabled",
42+
"otel.exporter.logging.traces.enabled",
43+
"otel.traces.exporter",
44+
"logging",
45+
false);
3846
}
39-
40-
@ConditionalOnProperty("otel.exporter.logging.enabled")
41-
static class LoggingEnabled {}
42-
43-
@ConditionalOnProperty("otel.exporter.logging.traces.enabled")
44-
static class LoggingTracesEnabled {}
4547
}
4648
}

0 commit comments

Comments
 (0)