From 36a4b36ccbe92c361361d4f2337a8bdfd0938b0b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 26 Sep 2022 17:27:27 +0100 Subject: [PATCH] Polish "Add support for MDC, Context Propagation (via B3 and W3C), and Baggage" See gh-32480 --- .../tracing/BraveAutoConfiguration.java | 137 +++++++-------- .../OpenTelemetryAutoConfiguration.java | 157 ++++++++---------- .../tracing/TracingProperties.java | 2 +- ...> BaggagePropagationIntegrationTests.java} | 5 +- .../tracing/BraveAutoConfigurationTests.java | 58 ++++--- .../OpenTelemetryAutoConfigurationTests.java | 88 +++++++--- 6 files changed, 235 insertions(+), 212 deletions(-) rename spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/{BaggageAutoConfigurationTests.java => BaggagePropagationIntegrationTests.java} (97%) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfiguration.java index ee702d6d1a0..670f7caf876 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfiguration.java @@ -24,6 +24,7 @@ import brave.Tracing.Builder; import brave.TracingCustomizer; import brave.baggage.BaggageField; import brave.baggage.BaggagePropagation; +import brave.baggage.BaggagePropagation.FactoryBuilder; import brave.baggage.BaggagePropagationConfig; import brave.baggage.BaggagePropagationCustomizer; import brave.baggage.CorrelationScopeConfig; @@ -42,7 +43,6 @@ import brave.propagation.B3Propagation; import brave.propagation.CurrentTraceContext; import brave.propagation.CurrentTraceContext.ScopeDecorator; import brave.propagation.CurrentTraceContextCustomizer; -import brave.propagation.Propagation; import brave.propagation.Propagation.Factory; import brave.propagation.ThreadLocalCurrentTraceContext; import brave.sampler.Sampler; @@ -53,18 +53,17 @@ import io.micrometer.tracing.brave.bridge.BraveHttpServerHandler; import io.micrometer.tracing.brave.bridge.BravePropagator; import io.micrometer.tracing.brave.bridge.BraveTracer; import io.micrometer.tracing.brave.bridge.W3CPropagation; -import org.slf4j.MDC; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; /** @@ -175,29 +174,17 @@ public class BraveAutoConfiguration { } @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false", matchIfMissing = true) + @ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false") static class BraveNoBaggageConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C", - matchIfMissing = true) - Factory w3cPropagationNoBaggageFactory() { - return new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of()); // TODO: Use - // snapshots - // of - // tracing - // to not - // use - // baggage - // for W3C - } - - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3") - Factory b3PropagationNoBaggageFactory() { - return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); + Factory propagationFactory(TracingProperties tracing) { + return switch (tracing.getPropagation().getType()) { + case B3 -> + B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); + case W3C -> new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of()); + }; } } @@ -206,74 +193,70 @@ public class BraveAutoConfiguration { @ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true) static class BraveBaggageConfiguration { - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C", - matchIfMissing = true) - BaggagePropagation.FactoryBuilder w3cPropagationFactory() { - return BaggagePropagation.newFactoryBuilder(new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of())); + private final TracingProperties tracingProperties; + + BraveBaggageConfiguration(TracingProperties tracingProperties) { + this.tracingProperties = tracingProperties; } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3") - BaggagePropagation.FactoryBuilder b3PropagationFactory() { - return BaggagePropagation.newFactoryBuilder( - B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build()); + BaggagePropagation.FactoryBuilder propagationFactoryBuilder( + ObjectProvider baggagePropagationCustomizers) { + Factory delegate = switch (this.tracingProperties.getPropagation().getType()) { + case B3 -> + B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); + case W3C -> new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of()); + }; + FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(delegate); + baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); + return builder; + } + + @Bean + @Order(0) + BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer() { + return (builder) -> { + List remoteFields = this.tracingProperties.getBaggage().getRemoteFields(); + for (String fieldName : remoteFields) { + builder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName))); + } + }; } @Bean @ConditionalOnMissingBean - Propagation.Factory micrometerTracingPropagationWithBaggage(BaggagePropagation.FactoryBuilder factoryBuilder, - TracingProperties tracingProperties, - ObjectProvider> baggagePropagationCustomizers) { - List remoteFields = tracingProperties.getBaggage().getRemoteFields(); - for (String fieldName : remoteFields) { - factoryBuilder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName))); - } - baggagePropagationCustomizers.ifAvailable( - (customizers) -> customizers.forEach((customizer) -> customizer.customize(factoryBuilder))); + Factory propagationFactory(BaggagePropagation.FactoryBuilder factoryBuilder) { return factoryBuilder.build(); } - @Bean - @ConditionalOnMissingBean(CorrelationScopeDecorator.class) - @ConditionalOnBean(CorrelationScopeDecorator.Builder.class) - @ConditionalOnProperty(value = "management.tracing.baggage.correlation.enabled", matchIfMissing = true) - ScopeDecorator correlationFieldsCorrelationScopeDecorator(TracingProperties properties, - ObjectProvider> correlationScopeCustomizers, - CorrelationScopeDecorator.Builder builder) { - List correlationFields = properties.getBaggage().getCorrelation().getFields(); - for (String field : correlationFields) { - builder.add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(BaggageField.create(field)) - .flushOnUpdate().build()); - } - correlationScopeCustomizers - .ifAvailable((customizers) -> customizers.forEach((customizer) -> customizer.customize(builder))); - return builder.build(); - } - - @Bean - @ConditionalOnMissingBean(CorrelationScopeDecorator.class) - @ConditionalOnBean(CorrelationScopeDecorator.Builder.class) - @ConditionalOnProperty(value = "management.tracing.baggage.correlation.enabled", havingValue = "false") - ScopeDecorator noCorrelationFieldsCorrelationScopeDecorator(CorrelationScopeDecorator.Builder builder, - ObjectProvider> correlationScopeCustomizers) { - correlationScopeCustomizers - .ifAvailable((customizers) -> customizers.forEach((customizer) -> customizer.customize(builder))); - return builder.build(); - } - - } - - @Configuration(proxyBeanMethods = false) - static class CorrelationScopeDecoratorConfiguration { - @Bean @ConditionalOnMissingBean - @ConditionalOnClass(MDC.class) - CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder() { - return MDCScopeDecorator.newBuilder(); + CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder( + ObjectProvider correlationScopeCustomizers) { + CorrelationScopeDecorator.Builder builder = MDCScopeDecorator.newBuilder(); + correlationScopeCustomizers.forEach((customizer) -> customizer.customize(builder)); + return builder; + } + + @Bean + @Order(0) + @ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled", + matchIfMissing = true) + CorrelationScopeCustomizer correlationFieldsCorrelationScopeCustomizer() { + return (builder) -> { + List correlationFields = this.tracingProperties.getBaggage().getCorrelation().getFields(); + for (String field : correlationFields) { + builder.add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(BaggageField.create(field)) + .flushOnUpdate().build()); + } + }; + } + + @Bean + @ConditionalOnMissingBean(CorrelationScopeDecorator.class) + ScopeDecorator correlationScopeDecorator(CorrelationScopeDecorator.Builder builder) { + return builder.build(); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java index 55a17e86b9d..c5efef0e6c7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java @@ -53,8 +53,8 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; -import org.slf4j.MDC; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.SpringBootVersion; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -83,6 +83,12 @@ public class OpenTelemetryAutoConfiguration { */ private static final String DEFAULT_APPLICATION_NAME = "application"; + private final TracingProperties tracingProperties; + + OpenTelemetryAutoConfiguration(TracingProperties tracingProperties) { + this.tracingProperties = tracingProperties; + } + @Bean @ConditionalOnMissingBean OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) { @@ -92,32 +98,31 @@ public class OpenTelemetryAutoConfiguration { @Bean @ConditionalOnMissingBean - SdkTracerProvider otelSdkTracerProvider(Environment environment, List spanProcessors, + SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider spanProcessors, Sampler sampler) { String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME); SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler) .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName))); - for (SpanProcessor spanProcessor : spanProcessors) { - builder.addSpanProcessor(spanProcessor); - } + spanProcessors.orderedStream().forEach(builder::addSpanProcessor); return builder.build(); } @Bean @ConditionalOnMissingBean - ContextPropagators otelContextPropagators(List textMapPropagators) { - return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators)); + ContextPropagators otelContextPropagators(ObjectProvider textMapPropagators) { + return ContextPropagators + .create(TextMapPropagator.composite(textMapPropagators.orderedStream().collect(Collectors.toList()))); } @Bean @ConditionalOnMissingBean - Sampler otelSampler(TracingProperties properties) { - return Sampler.traceIdRatioBased(properties.getSampling().getProbability()); + Sampler otelSampler() { + return Sampler.traceIdRatioBased(this.tracingProperties.getSampling().getProbability()); } @Bean - SpanProcessor otelSpanProcessor(List spanExporter) { - return SpanProcessor.composite(spanExporter.stream() + SpanProcessor otelSpanProcessor(ObjectProvider spanExporters) { + return SpanProcessor.composite(spanExporters.orderedStream() .map((exporter) -> BatchSpanProcessor.builder(exporter).build()).collect(Collectors.toList())); } @@ -130,9 +135,9 @@ public class OpenTelemetryAutoConfiguration { @Bean @ConditionalOnMissingBean OtelTracer micrometerOtelTracer(Tracer tracer, EventPublisher eventPublisher, - OtelCurrentTraceContext otelCurrentTraceContext, TracingProperties properties) { - return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher, - new OtelBaggageManager(otelCurrentTraceContext, properties.getBaggage().getRemoteFields(), List.of())); + OtelCurrentTraceContext otelCurrentTraceContext) { + return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher, new OtelBaggageManager( + otelCurrentTraceContext, this.tracingProperties.getBaggage().getRemoteFields(), List.of())); } @Bean @@ -168,93 +173,65 @@ public class OpenTelemetryAutoConfiguration { new DefaultHttpServerAttributesExtractor()); } - @Configuration(proxyBeanMethods = false) - static class PropagationConfiguration { - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(B3Propagator.class) - static class B3NoBaggagePropagatorConfiguration { - - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3") - B3Propagator b3TextMapPropagator() { - return B3Propagator.injectingSingleHeader(); - } - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false") - static class W3CNoBaggagePropagatorConfiguration { - - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C", - matchIfMissing = true) - W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() { - return W3CTraceContextPropagator.getInstance(); - } - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true) - static class W3CBaggagePropagatorConfiguration { - - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C", - matchIfMissing = true) - TextMapPropagator w3cTextMapPropagatorWithBaggage() { - return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), - W3CBaggagePropagator.getInstance()); - } - - } - + @Bean + @ConditionalOnMissingBean + Slf4JEventListener otelSlf4JEventListener() { + return new Slf4JEventListener(); } @Configuration(proxyBeanMethods = false) - static class MicrometerTracingPropagationConfiguration { + @ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", matchIfMissing = true) + static class BaggageConfiguration { - @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true) - static class BaggagePropagatorConfiguration { - - @Bean - @ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3") - BaggageTextMapPropagator b3BaggageTextMapPropagator(TracingProperties properties, - OtelCurrentTraceContext otelCurrentTraceContext) { - return new BaggageTextMapPropagator(properties.getBaggage().getRemoteFields(), new OtelBaggageManager( - otelCurrentTraceContext, properties.getBaggage().getRemoteFields(), List.of())); - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(MDC.class) - static class Slf4jConfiguration { - - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty(value = "management.tracing.baggage.correlation.enabled", matchIfMissing = true) - Slf4JBaggageEventListener otelSlf4JBaggageEventListener(TracingProperties tracingProperties) { - return new Slf4JBaggageEventListener(tracingProperties.getBaggage().getCorrelation().getFields()); - } - - } + private final TracingProperties tracingProperties; + BaggageConfiguration(TracingProperties tracingProperties) { + this.tracingProperties = tracingProperties; } - } + @Bean + @ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "W3C", + matchIfMissing = true) + TextMapPropagator w3cTextMapPropagatorWithBaggage() { + return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), + W3CBaggagePropagator.getInstance()); + } - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(MDC.class) - static class Slf4jConfiguration { + @Bean + @ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "B3") + TextMapPropagator b3BaggageTextMapPropagator(OtelCurrentTraceContext otelCurrentTraceContext) { + List remoteFields = this.tracingProperties.getBaggage().getRemoteFields(); + return TextMapPropagator.composite(B3Propagator.injectingSingleHeader(), new BaggageTextMapPropagator( + remoteFields, new OtelBaggageManager(otelCurrentTraceContext, remoteFields, List.of()))); + } @Bean @ConditionalOnMissingBean - Slf4JEventListener otelSlf4JEventListener() { - return new Slf4JEventListener(); + @ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled", + matchIfMissing = true) + Slf4JBaggageEventListener otelSlf4JBaggageEventListener() { + return new Slf4JBaggageEventListener(this.tracingProperties.getBaggage().getCorrelation().getFields()); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", havingValue = "false") + static class NoBaggageConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "B3") + B3Propagator b3TextMapPropagator() { + return B3Propagator.injectingSingleHeader(); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "W3C", + matchIfMissing = true) + W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() { + return W3CTraceContextPropagator.getInstance(); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java index 51a801045e8..1aca4a53394 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java @@ -79,7 +79,7 @@ public class TracingProperties { /** * Whether to enable Micrometer Tracing baggage propagation. */ - private boolean enabled; + private boolean enabled = true; /** * Correlation configuration. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggageAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java similarity index 97% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggageAutoConfigurationTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java index f104fad7d55..2d4fb67902d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggageAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java @@ -36,11 +36,12 @@ import org.springframework.context.ApplicationContext; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for Baggage configuration. + * Tests for Baggage propagation with Brave and OpenTelemetry using W3C and B3 propagation + * formats. * * @author Marcin Grzejszczak */ -class BaggageAutoConfigurationTests { +class BaggagePropagationIntegrationTests { static final String COUNTRY_CODE = "country-code"; static final String BUSINESS_PROCESS = "bp"; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfigurationTests.java index baecb0bb3bc..1bac894b1b5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfigurationTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.tracing; import brave.Tracer; import brave.Tracing; import brave.baggage.BaggagePropagation; +import brave.baggage.CorrelationScopeConfig.SingleCorrelationField; import brave.http.HttpClientHandler; import brave.http.HttpClientRequest; import brave.http.HttpClientResponse; @@ -27,6 +28,7 @@ import brave.http.HttpServerRequest; import brave.http.HttpServerResponse; import brave.http.HttpTracing; import brave.propagation.CurrentTraceContext; +import brave.propagation.CurrentTraceContext.ScopeDecorator; import brave.propagation.Propagation; import brave.propagation.Propagation.Factory; import brave.sampler.Sampler; @@ -34,6 +36,8 @@ import io.micrometer.tracing.brave.bridge.BraveBaggageManager; import io.micrometer.tracing.brave.bridge.BraveHttpClientHandler; import io.micrometer.tracing.brave.bridge.BraveHttpServerHandler; import io.micrometer.tracing.brave.bridge.BraveTracer; +import io.micrometer.tracing.brave.bridge.W3CPropagation; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.mockito.Answers; @@ -59,6 +63,7 @@ class BraveAutoConfigurationTests { @Test void shouldSupplyDefaultBeans() { this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(BraveAutoConfiguration.class); assertThat(context).hasSingleBean(Tracing.class); assertThat(context).hasSingleBean(Tracer.class); assertThat(context).hasSingleBean(CurrentTraceContext.class); @@ -72,6 +77,9 @@ class BraveAutoConfigurationTests { assertThat(context).hasSingleBean(BraveHttpClientHandler.class); assertThat(context).hasSingleBean(Propagation.Factory.class); assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class); + assertThat(context).hasSingleBean(BraveTracer.class); + assertThat(context).hasSingleBean(BraveHttpServerHandler.class); + assertThat(context).hasSingleBean(BraveHttpClientHandler.class); }); } @@ -118,6 +126,7 @@ class BraveAutoConfigurationTests { }); } + @Test void shouldNotSupplyBeansIfBraveIsMissing() { this.contextRunner.withClassLoader(new FilteredClassLoader("brave")) .run((context) -> assertThat(context).doesNotHaveBean(BraveAutoConfiguration.class)); @@ -125,14 +134,15 @@ class BraveAutoConfigurationTests { @Test void shouldNotSupplyBeansIfMicrometerIsMissing() { - this.contextRunner.withClassLoader(new FilteredClassLoader("brave")) + this.contextRunner.withClassLoader(new FilteredClassLoader("io.micrometer")) .run((context) -> assertThat(context).doesNotHaveBean(BraveAutoConfiguration.class)); } @Test void shouldSupplyW3CPropagationFactoryByDefault() { this.contextRunner.run((context) -> { - assertThat(context).hasBean("w3cPropagationFactory"); + assertThat(context).hasBean("propagationFactory"); + assertThat(context).hasSingleBean(W3CPropagation.class); assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class); }); } @@ -140,7 +150,8 @@ class BraveAutoConfigurationTests { @Test void shouldSupplyB3PropagationFactoryViaProperty() { this.contextRunner.withPropertyValues("management.tracing.propagation.type=B3").run((context) -> { - assertThat(context).hasBean("b3PropagationFactory"); + assertThat(context).hasBean("propagationFactory"); + assertThat(context.getBean(Factory.class).toString()).isEqualTo("B3Propagation"); assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class); }); } @@ -151,14 +162,6 @@ class BraveAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(BraveAutoConfiguration.class)); } - @Test - void shouldNotSupplyMdcCorrelationScopeWhenMdcNotOnClasspath() { - this.contextRunner.withClassLoader(new FilteredClassLoader("org.slf4j")).run((context) -> { - assertThat(context).doesNotHaveBean("mdcCorrelationScopeDecoratorBuilder"); - assertThat(context).doesNotHaveBean("correlationScopeDecorator"); - }); - } - @Test void shouldNotSupplyCorrelationScopeDecoratorIfBaggageDisabled() { this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false") @@ -168,21 +171,38 @@ class BraveAutoConfigurationTests { @Test void shouldSupplyW3CWithoutBaggageByDefaultIfBaggageDisabled() { this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false") - .run((context) -> assertThat(context).hasBean("w3cPropagationNoBaggageFactory")); + .run((context) -> assertThat(context).hasSingleBean(W3CPropagation.class)); } @Test void shouldSupplyB3WithoutBaggageIfBaggageDisabledAndB3Picked() { - this.contextRunner - .withPropertyValues("management.tracing.baggage.enabled=false", - "management.tracing.propagation.type=B3") - .run((context) -> assertThat(context).hasBean("b3PropagationNoBaggageFactory")); + this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false", + "management.tracing.propagation.type=B3").run((context) -> { + assertThat(context).hasBean("propagationFactory"); + assertThat(context.getBean(Factory.class).toString()).isEqualTo("B3Propagation"); + }); } @Test - void shouldNotSupplyCorrelationScopeDecoratorIfBaggageCorrelationDisabled() { - this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=false") - .run((context) -> assertThat(context).doesNotHaveBean("correlationFieldsCorrelationScopeDecorator")); + void shouldNotApplyCorrelationFieldsIfBaggageCorrelationDisabled() { + this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=false", + "management.tracing.baggage.correlation.fields=alpha,bravo").run((context) -> { + ScopeDecorator scopeDecorator = context.getBean(ScopeDecorator.class); + assertThat(scopeDecorator) + .extracting("fields", InstanceOfAssertFactories.array(SingleCorrelationField[].class)) + .hasSize(2); + }); + } + + @Test + void shouldNotApplyCorrelationFieldsIfBaggageCorrelationEnabled() { + this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=true", + "management.tracing.baggage.correlation.fields=alpha,bravo").run((context) -> { + ScopeDecorator scopeDecorator = context.getBean(ScopeDecorator.class); + assertThat(scopeDecorator) + .extracting("fields", InstanceOfAssertFactories.array(SingleCorrelationField[].class)) + .hasSize(4); + }); } @Test diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java index 1c50fb33caf..21f3ed7e5c6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java @@ -16,9 +16,12 @@ package org.springframework.boot.actuate.autoconfigure.tracing; +import java.util.List; + import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; import io.micrometer.tracing.otel.bridge.OtelHttpClientHandler; import io.micrometer.tracing.otel.bridge.OtelHttpServerHandler; +import io.micrometer.tracing.otel.bridge.OtelPropagator; import io.micrometer.tracing.otel.bridge.OtelTracer; import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher; import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener; @@ -70,6 +73,11 @@ class OpenTelemetryAutoConfigurationTests { assertThat(context).hasSingleBean(ContextPropagators.class); assertThat(context).hasSingleBean(Sampler.class); assertThat(context).hasSingleBean(Tracer.class); + assertThat(context).hasSingleBean(Slf4JEventListener.class); + assertThat(context).hasSingleBean(Slf4JBaggageEventListener.class); + assertThat(context).hasSingleBean(SpanProcessor.class); + assertThat(context).hasSingleBean(OtelPropagator.class); + assertThat(context).hasSingleBean(TextMapPropagator.class); }); } @@ -86,8 +94,12 @@ class OpenTelemetryAutoConfigurationTests { assertThat(context).doesNotHaveBean(SdkTracerProvider.class); assertThat(context).doesNotHaveBean(ContextPropagators.class); assertThat(context).doesNotHaveBean(Sampler.class); - assertThat(context).doesNotHaveBean(SpanProcessor.class); assertThat(context).doesNotHaveBean(Tracer.class); + assertThat(context).doesNotHaveBean(Slf4JEventListener.class); + assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class); + assertThat(context).doesNotHaveBean(SpanProcessor.class); + assertThat(context).doesNotHaveBean(OtelPropagator.class); + assertThat(context).doesNotHaveBean(TextMapPropagator.class); }); } @@ -112,53 +124,63 @@ class OpenTelemetryAutoConfigurationTests { assertThat(context).hasSingleBean(ContextPropagators.class); assertThat(context).hasBean("customSampler"); assertThat(context).hasSingleBean(Sampler.class); - assertThat(context).hasBean("customSpanProcessor"); assertThat(context).hasBean("customTracer"); assertThat(context).hasSingleBean(Tracer.class); - }); - } - - @Test - void shouldSupplyBaggageAndSlf4jEventListenersWhenMdcOnClasspath() { - this.contextRunner.run((context) -> { + assertThat(context).hasBean("customSlf4jEventListener"); assertThat(context).hasSingleBean(Slf4JEventListener.class); + assertThat(context).hasBean("customSlf4jBaggageEventListener"); assertThat(context).hasSingleBean(Slf4JBaggageEventListener.class); + assertThat(context).hasBean("customOtelPropagator"); + assertThat(context).hasSingleBean(OtelPropagator.class); }); } @Test - void shouldSupplySlf4jEventListenersWhenMdcOnClasspathAndBaggageCorrelationDisabled() { - this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=false").run((context) -> { - assertThat(context).hasSingleBean(Slf4JEventListener.class); - assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class); + void shouldAllowMultipleSpanProcessors() { + this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { + assertThat(context.getBeansOfType(SpanProcessor.class)).hasSize(2); + assertThat(context).hasBean("customSpanProcessor"); }); } @Test - void shouldSupplySlf4jEventListenersWhenMdcOnClasspathAndBaggageDisabled() { - this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false").run((context) -> { - assertThat(context).hasSingleBean(Slf4JEventListener.class); - assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class); + void shouldAllowMultipleTextMapPropagators() { + this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { + assertThat(context.getBeansOfType(TextMapPropagator.class)).hasSize(2); + assertThat(context).hasBean("customTextMapPropagator"); }); } @Test - void shouldNotSupplySlf4jEventListenersWhenMdcNotOnClasspath() { - this.contextRunner.withClassLoader(new FilteredClassLoader("org.slf4j")).run((context) -> { - assertThat(context).doesNotHaveBean(Slf4JEventListener.class); - assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class); - }); + void shouldNotSupplySlf4jBaggageEventListenerBaggageCorrelationDisabled() { + this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class)); + } + + @Test + void shouldNotSupplySlf4JBaggageEventListenerWhenBaggageDisabled() { + this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class)); } @Test void shouldSupplyB3PropagationIfPropagationPropertySet() { this.contextRunner.withPropertyValues("management.tracing.propagation.type=B3").run((context) -> { - assertThat(context).hasSingleBean(B3Propagator.class); - assertThat(context).hasBean("b3TextMapPropagator"); + assertThat(context).hasBean("b3BaggageTextMapPropagator"); assertThat(context).doesNotHaveBean(W3CTraceContextPropagator.class); }); } + @Test + void shouldSupplyB3PropagationIfPropagationPropertySetAndBaggageDisabled() { + this.contextRunner.withPropertyValues("management.tracing.propagation.type=B3", + "management.tracing.baggage.enabled=false").run((context) -> { + assertThat(context).hasSingleBean(B3Propagator.class); + assertThat(context).hasBean("b3TextMapPropagator"); + assertThat(context).doesNotHaveBean(W3CTraceContextPropagator.class); + }); + } + @Test void shouldSupplyW3CPropagationWithBaggageByDefault() { this.contextRunner.run((context) -> assertThat(context).hasBean("w3cTextMapPropagatorWithBaggage")); @@ -238,6 +260,26 @@ class OpenTelemetryAutoConfigurationTests { return mock(Tracer.class); } + @Bean + Slf4JEventListener customSlf4jEventListener() { + return new Slf4JEventListener(); + } + + @Bean + Slf4JBaggageEventListener customSlf4jBaggageEventListener() { + return new Slf4JBaggageEventListener(List.of("alpha")); + } + + @Bean + OtelPropagator customOtelPropagator(ContextPropagators propagators, Tracer tracer) { + return new OtelPropagator(propagators, tracer); + } + + @Bean + TextMapPropagator customTextMapPropagator() { + return mock(TextMapPropagator.class); + } + } @Configuration(proxyBeanMethods = false)