From 4700a00d94793a7b37b2bf7fe63b59bb8667d473 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 17 Oct 2022 13:58:48 +0100 Subject: [PATCH] Auto-configure CompositeSpanHandler with Brave Closes gh-32753 --- .../tracing/BraveAutoConfiguration.java | 14 +++ .../tracing/BraveAutoConfigurationTests.java | 119 ++++++++++++++++++ 2 files changed, 133 insertions(+) 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 2d8e69e7fe..1d32b20d35 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 @@ -53,7 +53,11 @@ import io.micrometer.tracing.brave.bridge.BraveHttpClientHandler; 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.CompositeSpanHandler; 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 org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -64,6 +68,7 @@ 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.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; @@ -87,6 +92,15 @@ public class BraveAutoConfiguration { */ private static final String DEFAULT_APPLICATION_NAME = "application"; + @Bean + @ConditionalOnMissingBean + @Order(Ordered.HIGHEST_PRECEDENCE) + CompositeSpanHandler compositeSpanHandler(ObjectProvider predicates, + ObjectProvider reporters, ObjectProvider filters) { + return new CompositeSpanHandler(predicates.orderedStream().toList(), reporters.orderedStream().toList(), + filters.orderedStream().toList()); + } + @Bean @ConditionalOnMissingBean public Tracing braveTracing(Environment environment, List spanHandlers, 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 a08c030b2f..eecdf5516d 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 @@ -16,11 +16,14 @@ package org.springframework.boot.actuate.autoconfigure.tracing; +import java.util.Collections; + import brave.Span; import brave.Tracer; import brave.Tracing; import brave.baggage.BaggagePropagation; import brave.baggage.CorrelationScopeConfig.SingleCorrelationField; +import brave.handler.SpanHandler; import brave.http.HttpClientHandler; import brave.http.HttpClientRequest; import brave.http.HttpClientResponse; @@ -37,16 +40,22 @@ 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.CompositeSpanHandler; 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 org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.mockito.Answers; +import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfigurationTests.SpanHandlerConfiguration.AdditionalSpanHandler; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -81,6 +90,7 @@ class BraveAutoConfigurationTests { assertThat(context).hasSingleBean(BraveTracer.class); assertThat(context).hasSingleBean(BraveHttpServerHandler.class); assertThat(context).hasSingleBean(BraveHttpClientHandler.class); + assertThat(context).hasSingleBean(CompositeSpanHandler.class); }); } @@ -115,6 +125,8 @@ class BraveAutoConfigurationTests { assertThat(context).hasSingleBean(HttpServerHandler.class); assertThat(context).hasBean("customHttpClientHandler"); assertThat(context).hasSingleBean(HttpClientHandler.class); + assertThat(context).hasBean("customCompositeSpanHandler"); + assertThat(context).hasSingleBean(CompositeSpanHandler.class); }); } @@ -237,6 +249,108 @@ class BraveAutoConfigurationTests { }); } + @Test + @SuppressWarnings("rawtypes") + void compositeSpanHandlerShouldBeFirstSpanHandler() { + this.contextRunner.withUserConfiguration(SpanHandlerConfiguration.class).run((context) -> { + Tracing tracing = context.getBean(Tracing.class); + assertThat(tracing).extracting("tracer.spanHandler.delegate.handlers") + .asInstanceOf(InstanceOfAssertFactories.array(SpanHandler[].class)) + .extracting((handler) -> (Class) handler.getClass()) + .containsExactly(CompositeSpanHandler.class, AdditionalSpanHandler.class); + }); + } + + @Test + void compositeSpanHandlerUsesFilterPredicateAndReportersInOrder() { + this.contextRunner.withUserConfiguration(CompositeSpanHandlerComponentsConfiguration.class).run((context) -> { + CompositeSpanHandlerComponentsConfiguration components = context + .getBean(CompositeSpanHandlerComponentsConfiguration.class); + CompositeSpanHandler composite = context.getBean(CompositeSpanHandler.class); + assertThat(composite).extracting("spanFilters").asList().containsExactly(components.filter1, + components.filter2); + assertThat(composite).extracting("filters").asList().containsExactly(components.predicate2, + components.predicate1); + assertThat(composite).extracting("reporters").asList().containsExactly(components.reporter1, + components.reporter3, components.reporter2); + }); + } + + @Configuration(proxyBeanMethods = false) + static class CompositeSpanHandlerComponentsConfiguration { + + private final SpanFilter filter1 = mock(SpanFilter.class); + + private final SpanFilter filter2 = mock(SpanFilter.class); + + private final SpanExportingPredicate predicate1 = mock(SpanExportingPredicate.class); + + private final SpanExportingPredicate predicate2 = mock(SpanExportingPredicate.class); + + private final SpanReporter reporter1 = mock(SpanReporter.class); + + private final SpanReporter reporter2 = mock(SpanReporter.class); + + private final SpanReporter reporter3 = mock(SpanReporter.class); + + @Bean + @Order(1) + SpanFilter filter1() { + return this.filter1; + } + + @Bean + @Order(2) + SpanFilter filter2() { + return this.filter2; + } + + @Bean + @Order(2) + SpanExportingPredicate predicate1() { + return this.predicate1; + } + + @Bean + @Order(1) + SpanExportingPredicate predicate2() { + return this.predicate2; + } + + @Bean + @Order(1) + SpanReporter reporter1() { + return this.reporter1; + } + + @Bean + @Order(3) + SpanReporter reporter2() { + return this.reporter2; + } + + @Bean + @Order(2) + SpanReporter reporter3() { + return this.reporter3; + } + + } + + @Configuration(proxyBeanMethods = false) + static class SpanHandlerConfiguration { + + @Bean + SpanHandler additionalSpanHandler() { + return new AdditionalSpanHandler(); + } + + static class AdditionalSpanHandler extends SpanHandler { + + } + + } + @Configuration(proxyBeanMethods = false) private static class CustomConfiguration { @@ -302,6 +416,11 @@ class BraveAutoConfigurationTests { return mock(BraveHttpClientHandler.class); } + @Bean + CompositeSpanHandler customCompositeSpanHandler() { + return new CompositeSpanHandler(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + } + } }