From a0ceb2c3ff88e0ee6b20af8a10aed9c833634d7e Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Thu, 15 Sep 2022 13:24:29 -0700 Subject: [PATCH] Use TracingAwareMeterObservationHandler if tracing is configured See gh-32399 --- .../ObservationAutoConfiguration.java | 32 ++++++---- .../ObservationAutoConfigurationTests.java | 62 +++++++++++++++++++ 2 files changed, 81 insertions(+), 13 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration.java index 0288371938..ebff229197 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration.java @@ -20,19 +20,21 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler; import io.micrometer.core.instrument.observation.MeterObservationHandler; import io.micrometer.observation.GlobalObservationConvention; +import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationPredicate; import io.micrometer.observation.ObservationRegistry; -import io.micrometer.tracing.handler.TracingObservationHandler; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration; 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.ConditionalOnMissingClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -42,9 +44,10 @@ import org.springframework.context.annotation.Configuration; * * @author Moritz Halbritter * @author Brian Clozel + * @author Jonatan Ivanov * @since 3.0.0 */ -@AutoConfiguration(after = CompositeMeterRegistryAutoConfiguration.class) +@AutoConfiguration(after = { CompositeMeterRegistryAutoConfiguration.class, MicrometerTracingAutoConfiguration.class }) @ConditionalOnClass(ObservationRegistry.class) @EnableConfigurationProperties(ObservationProperties.class) public class ObservationAutoConfiguration { @@ -67,21 +70,16 @@ public class ObservationAutoConfiguration { } @Configuration(proxyBeanMethods = false) - @ConditionalOnBean(MeterRegistry.class) - static class MetricsConfiguration { + @ConditionalOnMissingBean(type = "io.micrometer.tracing.Tracer") + static class OnlyMetricsConfiguration { @Bean @ConditionalOnMissingBean(MeterObservationHandler.class) + @ConditionalOnBean(MeterRegistry.class) DefaultMeterObservationHandler defaultMeterObservationHandler(MeterRegistry meterRegistry) { return new DefaultMeterObservationHandler(meterRegistry); } - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnMissingClass("io.micrometer.tracing.handler.TracingObservationHandler") - static class OnlyMetricsConfiguration { - @Bean OnlyMetricsObservationHandlerGrouping onlyMetricsObservationHandlerGrouping() { return new OnlyMetricsObservationHandlerGrouping(); @@ -90,8 +88,16 @@ public class ObservationAutoConfiguration { } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(TracingObservationHandler.class) - static class TracingConfiguration { + @ConditionalOnBean(Tracer.class) + static class MetricsWithTracingConfiguration { + + @Bean + @ConditionalOnMissingBean(MeterObservationHandler.class) + @ConditionalOnBean(MeterRegistry.class) + TracingAwareMeterObservationHandler tracingAwareMeterObservationHandler( + MeterRegistry meterRegistry, Tracer tracer) { + return new TracingAwareMeterObservationHandler<>(new DefaultMeterObservationHandler(meterRegistry), tracer); + } @Bean TracingObservationHandlerGrouping tracingObservationHandlerGrouping() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfigurationTests.java index 4f074e919f..0790c883e4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfigurationTests.java @@ -34,6 +34,7 @@ import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObserv import io.micrometer.observation.ObservationPredicate; import io.micrometer.observation.ObservationRegistry; import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler; import io.micrometer.tracing.handler.TracingObservationHandler; import org.junit.jupiter.api.Test; import org.mockito.Answers; @@ -55,6 +56,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link ObservationAutoConfiguration}. * * @author Moritz Halbritter + * @author Jonatan Ivanov */ class ObservationAutoConfigurationTests { @@ -65,6 +67,16 @@ class ObservationAutoConfigurationTests { private final ApplicationContextRunner tracingContextRunner = new ApplicationContextRunner() .with(MetricsRun.simple()).withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class)); + @Test + void beansShouldNotBeSuppliedWhenMicrometerObservationIsNotOnClassPath() { + this.tracingContextRunner.withClassLoader(new FilteredClassLoader("io.micrometer.observation")) + .run((context) -> { + assertThat(context).hasSingleBean(MeterRegistry.class); + assertThat(context).doesNotHaveBean(ObservationRegistry.class); + assertThat(context).doesNotHaveBean(MeterObservationHandler.class); + }); + } + @Test void autoConfiguresDefaultMeterObservationHandler() { this.contextRunner.run((context) -> { @@ -75,9 +87,17 @@ class ObservationAutoConfigurationTests { // Observation leads to a timer MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); assertThat(meterRegistry.get("test-observation").timer().count()).isEqualTo(1); + assertThat(context).hasSingleBean(DefaultMeterObservationHandler.class); + assertThat(context.getBeansOfType(ObservationHandler.class)).hasSize(1); }); } + @Test + void allowsDefaultMeterObservationHandlerToBeDisabled() { + this.contextRunner.withClassLoader(new FilteredClassLoader(MeterRegistry.class)) + .run((context) -> assertThat(context).doesNotHaveBean(ObservationHandler.class)); + } + @Test void autoConfiguresObservationPredicates() { this.contextRunner.withUserConfiguration(ObservationPredicates.class).run((context) -> { @@ -119,6 +139,8 @@ class ObservationAutoConfigurationTests { assertThat(handlers.get(1)).isInstanceOf(CustomMeterObservationHandler.class); assertThat(((CustomMeterObservationHandler) handlers.get(1)).getName()) .isEqualTo("customMeterObservationHandler1"); + assertThat(context).doesNotHaveBean(DefaultMeterObservationHandler.class); + assertThat(context).doesNotHaveBean(TracingAwareMeterObservationHandler.class); }); } @@ -132,9 +154,26 @@ class ObservationAutoConfigurationTests { Observation.start("test-observation", () -> customContext, observationRegistry).stop(); assertThat(handlers).hasSize(1); assertThat(handlers.get(0)).isInstanceOf(ObservationHandlerWithCustomContext.class); + assertThat(context).hasSingleBean(DefaultMeterObservationHandler.class); + assertThat(context).doesNotHaveBean(TracingAwareMeterObservationHandler.class); }); } + @Test + void autoConfiguresTracingAwareMeterObservationHandler() { + this.tracingContextRunner.withUserConfiguration(CustomTracingObservationHandlers.class).run((context) -> { + ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class); + List> handlers = context.getBean(CalledHandlers.class).getCalledHandlers(); + // Intentionally not stopped since that will trigger additional logic in + // TracingAwareMeterObservationHandler that we don't test here + Observation.start("test-observation", observationRegistry); + assertThat(handlers).hasSize(1); + assertThat(handlers.get(0)).isInstanceOf(CustomTracingObservationHandler.class); + assertThat(context).hasSingleBean(TracingAwareMeterObservationHandler.class); + assertThat(context.getBeansOfType(ObservationHandler.class)).hasSize(2); + }); + } + @Test void autoConfiguresObservationHandlerWhenTracingIsActive() { this.tracingContextRunner.withUserConfiguration(ObservationHandlersTracing.class).run((context) -> { @@ -156,6 +195,8 @@ class ObservationAutoConfigurationTests { assertThat(handlers.get(2)).isInstanceOf(CustomMeterObservationHandler.class); assertThat(((CustomMeterObservationHandler) handlers.get(2)).getName()) .isEqualTo("customMeterObservationHandler1"); + assertThat(context).doesNotHaveBean(TracingAwareMeterObservationHandler.class); + assertThat(context).doesNotHaveBean(DefaultMeterObservationHandler.class); }); } @@ -236,6 +277,22 @@ class ObservationAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + @Import(CalledHandlersConfiguration.class) + static class CustomTracingObservationHandlers { + + @Bean + CustomTracingObservationHandler customTracingHandler1(CalledHandlers calledHandlers) { + return new CustomTracingObservationHandler("customTracingHandler1", calledHandlers); + } + + @Bean + Tracer tracer() { + return mock(Tracer.class); // simulating tracer configuration + } + + } + @Configuration(proxyBeanMethods = false) @Import(CalledHandlersConfiguration.class) static class ObservationHandlersTracing { @@ -282,6 +339,11 @@ class ObservationAutoConfigurationTests { return new CustomMeterObservationHandler("customMeterObservationHandler1", calledHandlers); } + @Bean + Tracer tracer() { + return mock(Tracer.class); // simulating tracer configuration + } + } private static class CustomTracingObservationHandler implements TracingObservationHandler {