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 91b1d6fa95a..0ccb94d55b5 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 @@ -21,9 +21,6 @@ import io.micrometer.core.instrument.config.MeterFilter; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; @@ -33,16 +30,10 @@ 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.ConditionalOnWebApplication; 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.http.server.reactive.observation.DefaultServerRequestObservationConvention; -import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention; -import org.springframework.web.filter.reactive.ServerHttpObservationFilter; /** * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring @@ -53,48 +44,22 @@ import org.springframework.web.filter.reactive.ServerHttpObservationFilter; * @author Dmytro Nosan * @since 3.0.0 */ -@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class, - SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class }) -@ConditionalOnClass(Observation.class) -@ConditionalOnBean(ObservationRegistry.class) +@AutoConfiguration(after = { SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class }) +@ConditionalOnClass({ Observation.class, MeterRegistry.class }) +@ConditionalOnBean({ ObservationRegistry.class, MeterRegistry.class }) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class }) -@SuppressWarnings("removal") public class WebFluxObservationAutoConfiguration { - private final ObservationProperties observationProperties; - - public WebFluxObservationAutoConfiguration(ObservationProperties observationProperties) { - this.observationProperties = observationProperties; - } - @Bean - @ConditionalOnMissingBean(ServerHttpObservationFilter.class) - @Order(Ordered.HIGHEST_PRECEDENCE + 1) - public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry, - ObjectProvider customConvention) { - String name = this.observationProperties.getHttp().getServer().getRequests().getName(); - ServerRequestObservationConvention convention = customConvention - .getIfAvailable(() -> new DefaultServerRequestObservationConvention(name)); - return new ServerHttpObservationFilter(registry, convention); - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(MeterRegistry.class) - @ConditionalOnBean(MeterRegistry.class) - static class MeterFilterConfiguration { - - @Bean - @Order(0) - MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties, - ObservationProperties observationProperties) { - String name = 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); - } - + @Order(0) + MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties, + ObservationProperties observationProperties) { + String name = 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); } } 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 48c065660fa..b5e05e99ea0 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 @@ -16,12 +16,12 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.reactive; -import java.util.List; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import io.micrometer.core.instrument.MeterRegistry; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import reactor.core.publisher.Mono; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; @@ -33,16 +33,6 @@ import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplic 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.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.filter.reactive.ServerHttpObservationFilter; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilter; -import org.springframework.web.server.WebFilterChain; import static org.assertj.core.api.Assertions.assertThat; @@ -54,7 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Madhura Bhave */ @ExtendWith(OutputCaptureExtension.class) -@SuppressWarnings("removal") class WebFluxObservationAutoConfigurationTests { private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner() @@ -62,31 +51,6 @@ class WebFluxObservationAutoConfigurationTests { .withConfiguration( AutoConfigurations.of(ObservationAutoConfiguration.class, WebFluxObservationAutoConfiguration.class)); - @Test - void shouldProvideWebFluxObservationFilter() { - this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ServerHttpObservationFilter.class)); - } - - @Test - void shouldProvideWebFluxObservationFilterOrdered() { - this.contextRunner.withBean(FirstWebFilter.class).withBean(ThirdWebFilter.class).run((context) -> { - List webFilters = context.getBeanProvider(WebFilter.class).orderedStream().toList(); - assertThat(webFilters.get(0)).isInstanceOf(FirstWebFilter.class); - assertThat(webFilters.get(1)).isInstanceOf(ServerHttpObservationFilter.class); - assertThat(webFilters.get(2)).isInstanceOf(ThirdWebFilter.class); - }); - } - - @Test - void shouldUseCustomConventionWhenAvailable() { - this.contextRunner.withUserConfiguration(CustomConventionConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(ServerHttpObservationFilter.class); - assertThat(context).getBean(ServerHttpObservationFilter.class) - .extracting("observationConvention") - .isInstanceOf(CustomConvention.class); - }); - } - @Test void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) { this.contextRunner.withUserConfiguration(TestController.class) @@ -108,7 +72,7 @@ class WebFluxObservationAutoConfigurationTests { .withPropertyValues("management.metrics.web.server.max-uri-tags=2", "management.observations.http.server.requests.name=my.http.server.requests") .run((context) -> { - MeterRegistry registry = getInitializedMeterRegistry(context); + MeterRegistry registry = getInitializedMeterRegistry(context, "my.http.server.requests"); assertThat(registry.get("my.http.server.requests").meters()).hasSizeLessThanOrEqualTo(2); assertThat(output).contains("Reached the maximum number of URI tags for 'my.http.server.requests'"); }); @@ -127,53 +91,17 @@ class WebFluxObservationAutoConfigurationTests { }); } - private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context) - throws Exception { - return getInitializedMeterRegistry(context, "/test0", "/test1", "/test2"); + private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context) { + return getInitializedMeterRegistry(context, "http.server.requests"); } - private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context, String... urls) - throws Exception { - assertThat(context).hasSingleBean(ServerHttpObservationFilter.class); - WebTestClient client = WebTestClient.bindToApplicationContext(context).build(); - for (String url : urls) { - client.get().uri(url).exchange().expectStatus().isOk(); - } - return context.getBean(MeterRegistry.class); - } - - @Configuration(proxyBeanMethods = false) - static class CustomConventionConfiguration { - - @Bean - CustomConvention customConvention() { - return new CustomConvention(); - } - - } - - static class CustomConvention extends DefaultServerRequestObservationConvention { - - } - - @Order(Ordered.HIGHEST_PRECEDENCE) - static class FirstWebFilter implements WebFilter { - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - return chain.filter(exchange); - } - - } - - @Order(Ordered.HIGHEST_PRECEDENCE + 2) - static class ThirdWebFilter implements WebFilter { - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - return chain.filter(exchange); - } - + private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context, + String metricName) { + MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); + meterRegistry.timer(metricName, "uri", "/test0").record(Duration.of(500, ChronoUnit.SECONDS)); + meterRegistry.timer(metricName, "uri", "/test1").record(Duration.of(500, ChronoUnit.SECONDS)); + meterRegistry.timer(metricName, "uri", "/test2").record(Duration.of(500, ChronoUnit.SECONDS)); + return meterRegistry; } } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc index fbae93af9cf..21c41f0c5d4 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc @@ -245,9 +245,6 @@ When it does so, the orders shown in the following table will be used: |=== | Web Filter | Order -| `ServerHttpObservationFilter` (Micrometer Observability) -| `Ordered.HIGHEST_PRECEDENCE + 1` - | `WebFilterChainProxy` (Spring Security) | `-100`