From cdf8d2e9c82381a74880a050db145942ca04ec1e Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 13 Feb 2026 13:36:55 +0100 Subject: [PATCH] Back off on Propagator in Brave and Otel auto-configurations This also fixes the bug that both auto-configurations couldn't be used together. Closes gh-49183 --- .../autoconfigure/BraveAutoConfiguration.java | 3 +- .../BraveAutoConfigurationTests.java | 21 ++++++++++ .../build.gradle | 2 + ...OpenTelemetryTracingAutoConfiguration.java | 3 +- ...elemetryTracingAutoConfigurationTests.java | 41 ++++++++++++++++++- 5 files changed, 66 insertions(+), 4 deletions(-) diff --git a/module/spring-boot-micrometer-tracing-brave/src/main/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfiguration.java b/module/spring-boot-micrometer-tracing-brave/src/main/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfiguration.java index 850644bb21d..2ef97d87ceb 100644 --- a/module/spring-boot-micrometer-tracing-brave/src/main/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfiguration.java +++ b/module/spring-boot-micrometer-tracing-brave/src/main/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfiguration.java @@ -39,6 +39,7 @@ import io.micrometer.tracing.brave.bridge.CompositeSpanHandler; import io.micrometer.tracing.exporter.SpanExportingPredicate; import io.micrometer.tracing.exporter.SpanFilter; import io.micrometer.tracing.exporter.SpanReporter; +import io.micrometer.tracing.propagation.Propagator; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -166,7 +167,7 @@ public final class BraveAutoConfiguration { } @Bean - @ConditionalOnMissingBean + @ConditionalOnMissingBean(Propagator.class) BravePropagator bravePropagator(Tracing tracing) { return new BravePropagator(tracing); } diff --git a/module/spring-boot-micrometer-tracing-brave/src/test/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-brave/src/test/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfigurationTests.java index aef08737410..e3daafbd45d 100644 --- a/module/spring-boot-micrometer-tracing-brave/src/test/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-brave/src/test/java/org/springframework/boot/micrometer/tracing/brave/autoconfigure/BraveAutoConfigurationTests.java @@ -39,6 +39,7 @@ import io.micrometer.observation.Observation; import io.micrometer.observation.Observation.Scope; import io.micrometer.observation.ObservationRegistry; import io.micrometer.tracing.brave.bridge.BraveBaggageManager; +import io.micrometer.tracing.brave.bridge.BravePropagator; import io.micrometer.tracing.brave.bridge.BraveSpanCustomizer; import io.micrometer.tracing.brave.bridge.BraveTracer; import io.micrometer.tracing.brave.bridge.CompositeSpanHandler; @@ -46,6 +47,7 @@ import io.micrometer.tracing.brave.bridge.W3CPropagation; import io.micrometer.tracing.exporter.SpanExportingPredicate; import io.micrometer.tracing.exporter.SpanFilter; import io.micrometer.tracing.exporter.SpanReporter; +import io.micrometer.tracing.propagation.Propagator; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; @@ -126,6 +128,15 @@ class BraveAutoConfigurationTests { this.contextRunner.run((context) -> assertThat(context).hasSingleBean(BraveTracer.class)); } + @Test + void shouldNotSupplyBravePropagatorWhenPropagatorBeanIsPresent() { + this.contextRunner.withUserConfiguration(PropagatorConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(Propagator.class); + assertThat(context).hasBean("customPropagator"); + assertThat(context).doesNotHaveBean(BravePropagator.class); + }); + } + @Test void shouldNotSupplyBeansIfBraveIsMissing() { this.contextRunner.withClassLoader(new FilteredClassLoader("brave")) @@ -535,4 +546,14 @@ class BraveAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class PropagatorConfiguration { + + @Bean + Propagator customPropagator() { + return mock(Propagator.class); + } + + } + } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/build.gradle b/module/spring-boot-micrometer-tracing-opentelemetry/build.gradle index 5de754f199f..5e254c833a4 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/build.gradle +++ b/module/spring-boot-micrometer-tracing-opentelemetry/build.gradle @@ -49,7 +49,9 @@ dependencies { dockerTestImplementation("org.testcontainers:testcontainers-junit-jupiter") testImplementation(project(":core:spring-boot-test")) + testImplementation(project(":module:spring-boot-micrometer-tracing-brave")) testImplementation(project(":test-support:spring-boot-test-support")) + testImplementation("io.micrometer:micrometer-tracing-bridge-brave") testImplementation("com.squareup.okhttp3:mockwebserver") testImplementation("io.micrometer:micrometer-registry-prometheus") testImplementation("io.opentelemetry:opentelemetry-exporter-common") diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java index 333f616fda5..8b2b2322964 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java @@ -31,6 +31,7 @@ import io.micrometer.tracing.otel.bridge.OtelSpanCustomizer; import io.micrometer.tracing.otel.bridge.OtelTracer; import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher; import io.micrometer.tracing.otel.bridge.Slf4JEventListener; +import io.micrometer.tracing.propagation.Propagator; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.Tracer; @@ -164,7 +165,7 @@ public final class OpenTelemetryTracingAutoConfiguration { } @Bean - @ConditionalOnMissingBean + @ConditionalOnMissingBean(Propagator.class) OtelPropagator otelPropagator(ContextPropagators contextPropagators, Tracer tracer) { return new OtelPropagator(contextPropagators, tracer); } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java index 642d9711a7b..d87f33315ff 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java @@ -27,6 +27,8 @@ import java.util.concurrent.TimeoutException; import io.micrometer.tracing.SpanCustomizer; import io.micrometer.tracing.Tracer.SpanInScope; +import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; +import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; import io.micrometer.tracing.otel.bridge.EventListener; import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; import io.micrometer.tracing.otel.bridge.OtelPropagator; @@ -36,6 +38,7 @@ import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher; import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener; import io.micrometer.tracing.otel.bridge.Slf4JEventListener; import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator; +import io.micrometer.tracing.propagation.Propagator; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.MeterProvider; @@ -63,6 +66,8 @@ import org.mockito.Mockito; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; import org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration; +import org.springframework.boot.micrometer.tracing.brave.autoconfigure.BraveAutoConfiguration; +import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.classpath.ForkedClassPath; @@ -89,8 +94,7 @@ import static org.mockito.Mockito.mock; class OpenTelemetryTracingAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of( - org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class, OpenTelemetryTracingAutoConfiguration.class)); @Test @@ -179,6 +183,29 @@ class OpenTelemetryTracingAutoConfigurationTests { }); } + @Test + void shouldNotSupplyOtelPropagatorWhenPropagatorBeanIsPresent() { + this.contextRunner.withUserConfiguration(PropagatorConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(Propagator.class); + assertThat(context).hasBean("customPropagator"); + assertThat(context).doesNotHaveBean(OtelPropagator.class); + }); + } + + @Test + void shouldWorkWithBraveAutoConfigurationWhenBothConfigurationsAreOnClasspath() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class, + OpenTelemetryTracingAutoConfiguration.class, BraveAutoConfiguration.class, + ObservationAutoConfiguration.class, MicrometerTracingAutoConfiguration.class)) + .run((context) -> { + assertThat(context).hasSingleBean(io.micrometer.tracing.Tracer.class); + assertThat(context).hasSingleBean(Propagator.class); + assertThat(context).hasSingleBean(PropagatingSenderTracingObservationHandler.class); + assertThat(context).hasSingleBean(PropagatingReceiverTracingObservationHandler.class); + }); + } + @Test void shouldSetupDefaultResourceAttributes() { this.contextRunner @@ -518,6 +545,16 @@ class OpenTelemetryTracingAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class PropagatorConfiguration { + + @Bean + Propagator customPropagator() { + return mock(Propagator.class); + } + + } + @Configuration(proxyBeanMethods = false) private static final class SdkTracerProviderCustomizationConfiguration {