diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java index bb1c806a3ee3..23bd226fd515 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java @@ -38,6 +38,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; import org.springframework.core.env.Environment; /** @@ -57,18 +58,26 @@ public class OpenTelemetryAutoConfiguration { public OpenTelemetryAutoConfiguration() {} + @Bean + @ConfigurationPropertiesBinding + OtelMapConverter otelMapConverter() { + // This is needed for otlp exporter headers and OtelResourceProperties. + + // We need this converter, even if the SDK is disabled, + // because the properties are parsed before the SDK is disabled. + + // We also need this converter if the OpenTelemetry bean is user supplied, + // because the environment variables may still contain a value that needs to be converted, + // even if the SDK is disabled (and the value thus ignored). + return new OtelMapConverter(); + } + @Configuration @Conditional(SdkEnabled.class) + @DependsOn("otelMapConverter") @ConditionalOnMissingBean(OpenTelemetry.class) static class OpenTelemetrySdkConfig { - @Bean - @ConfigurationPropertiesBinding - public OtelMapConverter otelMapConverter() { - // needed for otlp exporter headers and OtelResourceProperties - return new OtelMapConverter(); - } - @Bean public OpenTelemetrySdkComponentLoader openTelemetrySdkComponentLoader( ApplicationContext applicationContext) { @@ -129,21 +138,10 @@ public ConfigProperties otelProperties( } @Configuration + @DependsOn("otelMapConverter") @ConditionalOnMissingBean(OpenTelemetry.class) @ConditionalOnProperty(name = "otel.sdk.disabled", havingValue = "true") static class DisabledOpenTelemetrySdkConfig { - - @Bean - @ConfigurationPropertiesBinding - // Duplicated in OpenTelemetrySdkConfig and DisabledOpenTelemetrySdkConfig to not expose the - // converter in the public API - public OtelMapConverter otelMapConverter() { - // needed for otlp exporter headers and OtelResourceProperties - // we need this converter, even if the SDK is disabled, - // because the properties are parsed before the SDK is disabled - return new OtelMapConverter(); - } - @Bean public OpenTelemetry openTelemetry() { return OpenTelemetry.noop(); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java index 8bb38dc453cf..9754ccb3b7ad 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; @@ -77,6 +78,19 @@ void mapFlatHeaders(String key) { .containsExactly(entry("a", "1"), entry("b", "2"))); } + @ParameterizedTest + @MethodSource("headerKeys") + @DisplayName("should map headers from spring properties with user supplied OpenTelemetry bean") + void mapFlatHeadersWithUserSuppliedOtelBean(String key) { + this.contextRunner + .withSystemProperties(key + "=a=1,b=2") + .withBean(OpenTelemetry.class, OpenTelemetry::noop) + .run( + context -> + assertThat(getConfig(context).getMap(key)) + .containsExactly(entry("a", "1"), entry("b", "2"))); + } + @ParameterizedTest @MethodSource("headerKeys") @DisplayName("should map headers from spring application.yaml")