diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java index 149141c887..40721dcbb9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java @@ -17,17 +17,26 @@ package org.springframework.boot.actuate.autoconfigure.tracing; import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.annotation.DefaultNewSpanParser; +import io.micrometer.tracing.annotation.ImperativeMethodInvocationProcessor; +import io.micrometer.tracing.annotation.MethodInvocationProcessor; +import io.micrometer.tracing.annotation.NewSpanParser; +import io.micrometer.tracing.annotation.SpanAspect; +import io.micrometer.tracing.annotation.SpanTagAnnotationHandler; import io.micrometer.tracing.handler.DefaultTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; import io.micrometer.tracing.propagation.Propagator; +import org.aspectj.weaver.Advice; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -35,10 +44,12 @@ import org.springframework.core.annotation.Order; * {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Tracing API. * * @author Moritz Halbritter + * @author Jonatan Ivanov * @since 3.0.0 */ @AutoConfiguration @ConditionalOnClass(Tracer.class) +@ConditionalOnBean(Tracer.class) public class MicrometerTracingAutoConfiguration { /** @@ -60,7 +71,6 @@ public class MicrometerTracingAutoConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnBean(Tracer.class) @Order(DEFAULT_TRACING_OBSERVATION_HANDLER_ORDER) public DefaultTracingObservationHandler defaultTracingObservationHandler(Tracer tracer) { return new DefaultTracingObservationHandler(tracer); @@ -68,7 +78,7 @@ public class MicrometerTracingAutoConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnBean({ Tracer.class, Propagator.class }) + @ConditionalOnBean(Propagator.class) @Order(SENDER_TRACING_OBSERVATION_HANDLER_ORDER) public PropagatingSenderTracingObservationHandler propagatingSenderTracingObservationHandler(Tracer tracer, Propagator propagator) { @@ -77,11 +87,39 @@ public class MicrometerTracingAutoConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnBean({ Tracer.class, Propagator.class }) + @ConditionalOnBean(Propagator.class) @Order(RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER) public PropagatingReceiverTracingObservationHandler propagatingReceiverTracingObservationHandler(Tracer tracer, Propagator propagator) { return new PropagatingReceiverTracingObservationHandler<>(tracer, propagator); } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(Advice.class) + static class SpanAspectConfiguration { + + @Bean + @ConditionalOnMissingBean + DefaultNewSpanParser newSpanParser() { + return new DefaultNewSpanParser(); + } + + @Bean + @ConditionalOnMissingBean + ImperativeMethodInvocationProcessor imperativeMethodInvocationProcessor(NewSpanParser newSpanParser, + Tracer tracer, ObjectProvider spanTagAnnotationHandler) { + ImperativeMethodInvocationProcessor methodInvocationProcessor = new ImperativeMethodInvocationProcessor( + newSpanParser, tracer); + spanTagAnnotationHandler.ifAvailable(methodInvocationProcessor::setSpanTagAnnotationHandler); + return methodInvocationProcessor; + } + + @Bean + @ConditionalOnMissingBean + SpanAspect spanAspect(MethodInvocationProcessor methodInvocationProcessor) { + return new SpanAspect(methodInvocationProcessor); + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java index d9f94f4639..3ca8ff2364 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java @@ -19,18 +19,27 @@ package org.springframework.boot.actuate.autoconfigure.tracing; import java.util.List; import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.annotation.DefaultNewSpanParser; +import io.micrometer.tracing.annotation.ImperativeMethodInvocationProcessor; +import io.micrometer.tracing.annotation.MethodInvocationProcessor; +import io.micrometer.tracing.annotation.NewSpanParser; +import io.micrometer.tracing.annotation.SpanAspect; +import io.micrometer.tracing.annotation.SpanTagAnnotationHandler; import io.micrometer.tracing.handler.DefaultTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; import io.micrometer.tracing.handler.TracingObservationHandler; import io.micrometer.tracing.propagation.Propagator; +import org.aspectj.weaver.Advice; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -39,6 +48,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link MicrometerTracingAutoConfiguration}. * * @author Moritz Halbritter + * @author Jonatan Ivanov */ class MicrometerTracingAutoConfigurationTests { @@ -52,6 +62,9 @@ class MicrometerTracingAutoConfigurationTests { assertThat(context).hasSingleBean(DefaultTracingObservationHandler.class); assertThat(context).hasSingleBean(PropagatingReceiverTracingObservationHandler.class); assertThat(context).hasSingleBean(PropagatingSenderTracingObservationHandler.class); + assertThat(context).hasSingleBean(DefaultNewSpanParser.class); + assertThat(context).hasSingleBean(ImperativeMethodInvocationProcessor.class); + assertThat(context).hasSingleBean(SpanAspect.class); }); } @@ -75,14 +88,21 @@ class MicrometerTracingAutoConfigurationTests { @Test void shouldBackOffOnCustomBeans() { - this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { - assertThat(context).hasBean("customDefaultTracingObservationHandler"); - assertThat(context).hasSingleBean(DefaultTracingObservationHandler.class); - assertThat(context).hasBean("customPropagatingReceiverTracingObservationHandler"); - assertThat(context).hasSingleBean(PropagatingReceiverTracingObservationHandler.class); - assertThat(context).hasBean("customPropagatingSenderTracingObservationHandler"); - assertThat(context).hasSingleBean(PropagatingSenderTracingObservationHandler.class); - }); + this.contextRunner.withUserConfiguration(TracerConfiguration.class, CustomConfiguration.class) + .run((context) -> { + assertThat(context).hasBean("customDefaultTracingObservationHandler"); + assertThat(context).hasSingleBean(DefaultTracingObservationHandler.class); + assertThat(context).hasBean("customPropagatingReceiverTracingObservationHandler"); + assertThat(context).hasSingleBean(PropagatingReceiverTracingObservationHandler.class); + assertThat(context).hasBean("customPropagatingSenderTracingObservationHandler"); + assertThat(context).hasSingleBean(PropagatingSenderTracingObservationHandler.class); + assertThat(context).hasBean("customDefaultNewSpanParser"); + assertThat(context).hasSingleBean(DefaultNewSpanParser.class); + assertThat(context).hasBean("customImperativeMethodInvocationProcessor"); + assertThat(context).hasSingleBean(ImperativeMethodInvocationProcessor.class); + assertThat(context).hasBean("customSpanAspect"); + assertThat(context).hasSingleBean(SpanAspect.class); + }); } @Test @@ -91,6 +111,9 @@ class MicrometerTracingAutoConfigurationTests { assertThat(context).doesNotHaveBean(DefaultTracingObservationHandler.class); assertThat(context).doesNotHaveBean(PropagatingReceiverTracingObservationHandler.class); assertThat(context).doesNotHaveBean(PropagatingSenderTracingObservationHandler.class); + assertThat(context).doesNotHaveBean(DefaultNewSpanParser.class); + assertThat(context).doesNotHaveBean(ImperativeMethodInvocationProcessor.class); + assertThat(context).doesNotHaveBean(SpanAspect.class); }); } @@ -100,17 +123,47 @@ class MicrometerTracingAutoConfigurationTests { assertThat(context).doesNotHaveBean(DefaultTracingObservationHandler.class); assertThat(context).doesNotHaveBean(PropagatingReceiverTracingObservationHandler.class); assertThat(context).doesNotHaveBean(PropagatingSenderTracingObservationHandler.class); + assertThat(context).doesNotHaveBean(DefaultNewSpanParser.class); + assertThat(context).doesNotHaveBean(ImperativeMethodInvocationProcessor.class); + assertThat(context).doesNotHaveBean(SpanAspect.class); }); } + @Test + void shouldNotSupplyBeansIfAspectjIsMissing() { + this.contextRunner.withUserConfiguration(TracerConfiguration.class) + .withClassLoader(new FilteredClassLoader(Advice.class)) + .run((context) -> { + assertThat(context).doesNotHaveBean(DefaultNewSpanParser.class); + assertThat(context).doesNotHaveBean(ImperativeMethodInvocationProcessor.class); + assertThat(context).doesNotHaveBean(SpanAspect.class); + }); + } + @Test void shouldNotSupplyBeansIfPropagatorIsMissing() { this.contextRunner.withUserConfiguration(TracerConfiguration.class).run((context) -> { assertThat(context).doesNotHaveBean(PropagatingSenderTracingObservationHandler.class); assertThat(context).doesNotHaveBean(PropagatingReceiverTracingObservationHandler.class); + + assertThat(context).hasSingleBean(DefaultNewSpanParser.class); + assertThat(context).hasSingleBean(ImperativeMethodInvocationProcessor.class); + assertThat(context).hasSingleBean(SpanAspect.class); }); } + @Test + void shouldConfigureSpanTagAnnotationHandler() { + this.contextRunner.withUserConfiguration(TracerConfiguration.class, SpanTagAnnotationHandlerConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(DefaultNewSpanParser.class); + assertThat(context).hasSingleBean(SpanAspect.class); + assertThat(ReflectionTestUtils.getField(context.getBean(ImperativeMethodInvocationProcessor.class), + "spanTagAnnotationHandler")) + .isSameAs(context.getBean(SpanTagAnnotationHandler.class)); + }); + } + @Configuration(proxyBeanMethods = false) private static class TracerConfiguration { @@ -149,6 +202,34 @@ class MicrometerTracingAutoConfigurationTests { return mock(PropagatingSenderTracingObservationHandler.class); } + @Bean + DefaultNewSpanParser customDefaultNewSpanParser() { + return new DefaultNewSpanParser(); + } + + @Bean + @ConditionalOnMissingBean + ImperativeMethodInvocationProcessor customImperativeMethodInvocationProcessor(NewSpanParser newSpanParser, + Tracer tracer) { + return new ImperativeMethodInvocationProcessor(newSpanParser, tracer); + } + + @Bean + @ConditionalOnMissingBean + SpanAspect customSpanAspect(MethodInvocationProcessor methodInvocationProcessor) { + return new SpanAspect(methodInvocationProcessor); + } + + } + + @Configuration(proxyBeanMethods = false) + private static class SpanTagAnnotationHandlerConfiguration { + + @Bean + SpanTagAnnotationHandler spanTagAnnotationHandler() { + return new SpanTagAnnotationHandler((aClass) -> null, (aClass) -> null); + } + } }