diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java index 0ccb94d55b5c..94d125f63f2a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,10 +30,13 @@ 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.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; +import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention; +import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention; /** * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring @@ -42,6 +45,7 @@ * @author Brian Clozel * @author Jon Schneider * @author Dmytro Nosan + * @author Moritz Halbritter * @since 3.0.0 */ @AutoConfiguration(after = { SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class }) @@ -51,15 +55,27 @@ @EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class }) public class WebFluxObservationAutoConfiguration { + private final ObservationProperties observationProperties; + + WebFluxObservationAutoConfiguration(ObservationProperties observationProperties) { + this.observationProperties = observationProperties; + } + @Bean @Order(0) - MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties, - ObservationProperties observationProperties) { - String name = observationProperties.getHttp().getServer().getRequests().getName(); + MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties) { + String name = this.observationProperties.getHttp().getServer().getRequests().getName(); MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter( () -> "Reached the maximum number of URI tags for '%s'.".formatted(name)); return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(), filter); } + @Bean + @ConditionalOnMissingBean(ServerRequestObservationConvention.class) + DefaultServerRequestObservationConvention defaultServerRequestObservationConvention() { + return new DefaultServerRequestObservationConvention( + this.observationProperties.getHttp().getServer().getRequests().getName()); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java index b5e05e99ea0a..384be8d4ae30 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,8 +33,11 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention; +import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link WebFluxObservationAutoConfiguration} @@ -42,6 +45,7 @@ * @author Brian Clozel * @author Dmytro Nosan * @author Madhura Bhave + * @author Moritz Halbritter */ @ExtendWith(OutputCaptureExtension.class) class WebFluxObservationAutoConfigurationTests { @@ -91,6 +95,28 @@ void shouldNotDenyNorLogIfMaxUrisIsNotReached(CapturedOutput output) { }); } + @Test + void shouldSupplyDefaultServerRequestObservationConvention() { + this.contextRunner.withPropertyValues("management.observations.http.server.requests.name=some-other-name") + .run((context) -> { + assertThat(context).hasSingleBean(DefaultServerRequestObservationConvention.class); + DefaultServerRequestObservationConvention bean = context + .getBean(DefaultServerRequestObservationConvention.class); + assertThat(bean.getName()).isEqualTo("some-other-name"); + }); + } + + @Test + void shouldBackOffOnCustomServerRequestObservationConvention() { + this.contextRunner + .withBean("customServerRequestObservationConvention", ServerRequestObservationConvention.class, + () -> mock(ServerRequestObservationConvention.class)) + .run((context) -> { + assertThat(context).hasBean("customServerRequestObservationConvention"); + assertThat(context).hasSingleBean(ServerRequestObservationConvention.class); + }); + } + private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context) { return getInitializedMeterRegistry(context, "http.server.requests"); }