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 27860e3b1be6..a998e6b5d24b 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 @@ -42,6 +42,7 @@ 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; @@ -77,6 +78,7 @@ public WebFluxObservationAutoConfiguration(MetricsProperties metricsProperties, @Bean @ConditionalOnMissingBean + @Order(Ordered.HIGHEST_PRECEDENCE + 1) public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry, ObjectProvider customConvention, ObjectProvider tagConfigurer, 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 d2cfe8c4d214..bcb3e1e75f96 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,11 +16,14 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.reactive; +import java.util.List; + import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; 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; @@ -37,10 +40,14 @@ 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; @@ -64,6 +71,16 @@ 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 shouldUseConventionAdapterWhenCustomTagsProvider() { this.contextRunner.withUserConfiguration(CustomTagsProviderConfiguration.class).run((context) -> { @@ -207,4 +224,24 @@ 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); + } + + } + } 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 3d77c1227b16..d7abfcb332cd 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 @@ -207,6 +207,9 @@ 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`