Skip to content

Commit c8d036e

Browse files
committed
Remove ServerHttpObservationFilter from WebFlux
This commit removes the auto-configuration of the `ServerHttpObservationFilter` bean for WebFlux applications as it's been deprecated by Spring Framework. The Observability instrumentation is now handled at the `WebHttpHandlerBuilder` in Framework directly and doesn't need any auto-configuration from Spring Boot. Closes gh-37344
1 parent 8a1f6d4 commit c8d036e

File tree

3 files changed

+23
-133
lines changed

3 files changed

+23
-133
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java

+11-46
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121
import io.micrometer.observation.Observation;
2222
import io.micrometer.observation.ObservationRegistry;
2323

24-
import org.springframework.beans.factory.ObjectProvider;
25-
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
26-
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2724
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
2825
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
2926
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
@@ -33,16 +30,10 @@
3330
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3431
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3532
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
36-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3733
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
3834
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3935
import org.springframework.context.annotation.Bean;
40-
import org.springframework.context.annotation.Configuration;
41-
import org.springframework.core.Ordered;
4236
import org.springframework.core.annotation.Order;
43-
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
44-
import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention;
45-
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
4637

4738
/**
4839
* {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring
@@ -53,48 +44,22 @@
5344
* @author Dmytro Nosan
5445
* @since 3.0.0
5546
*/
56-
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
57-
SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class })
58-
@ConditionalOnClass(Observation.class)
59-
@ConditionalOnBean(ObservationRegistry.class)
47+
@AutoConfiguration(after = { SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class })
48+
@ConditionalOnClass({ Observation.class, MeterRegistry.class })
49+
@ConditionalOnBean({ ObservationRegistry.class, MeterRegistry.class })
6050
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
6151
@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class })
62-
@SuppressWarnings("removal")
6352
public class WebFluxObservationAutoConfiguration {
6453

65-
private final ObservationProperties observationProperties;
66-
67-
public WebFluxObservationAutoConfiguration(ObservationProperties observationProperties) {
68-
this.observationProperties = observationProperties;
69-
}
70-
7154
@Bean
72-
@ConditionalOnMissingBean(ServerHttpObservationFilter.class)
73-
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
74-
public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry,
75-
ObjectProvider<ServerRequestObservationConvention> customConvention) {
76-
String name = this.observationProperties.getHttp().getServer().getRequests().getName();
77-
ServerRequestObservationConvention convention = customConvention
78-
.getIfAvailable(() -> new DefaultServerRequestObservationConvention(name));
79-
return new ServerHttpObservationFilter(registry, convention);
80-
}
81-
82-
@Configuration(proxyBeanMethods = false)
83-
@ConditionalOnClass(MeterRegistry.class)
84-
@ConditionalOnBean(MeterRegistry.class)
85-
static class MeterFilterConfiguration {
86-
87-
@Bean
88-
@Order(0)
89-
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties,
90-
ObservationProperties observationProperties) {
91-
String name = observationProperties.getHttp().getServer().getRequests().getName();
92-
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
93-
() -> "Reached the maximum number of URI tags for '%s'.".formatted(name));
94-
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(),
95-
filter);
96-
}
97-
55+
@Order(0)
56+
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties,
57+
ObservationProperties observationProperties) {
58+
String name = observationProperties.getHttp().getServer().getRequests().getName();
59+
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
60+
() -> "Reached the maximum number of URI tags for '%s'.".formatted(name));
61+
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(),
62+
filter);
9863
}
9964

10065
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java

+12-84
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.observation.web.reactive;
1818

19-
import java.util.List;
19+
import java.time.Duration;
20+
import java.time.temporal.ChronoUnit;
2021

2122
import io.micrometer.core.instrument.MeterRegistry;
2223
import org.junit.jupiter.api.Test;
2324
import org.junit.jupiter.api.extension.ExtendWith;
24-
import reactor.core.publisher.Mono;
2525

2626
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2727
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
@@ -33,16 +33,6 @@
3333
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
3434
import org.springframework.boot.test.system.CapturedOutput;
3535
import org.springframework.boot.test.system.OutputCaptureExtension;
36-
import org.springframework.context.annotation.Bean;
37-
import org.springframework.context.annotation.Configuration;
38-
import org.springframework.core.Ordered;
39-
import org.springframework.core.annotation.Order;
40-
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
41-
import org.springframework.test.web.reactive.server.WebTestClient;
42-
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
43-
import org.springframework.web.server.ServerWebExchange;
44-
import org.springframework.web.server.WebFilter;
45-
import org.springframework.web.server.WebFilterChain;
4636

4737
import static org.assertj.core.api.Assertions.assertThat;
4838

@@ -54,39 +44,13 @@
5444
* @author Madhura Bhave
5545
*/
5646
@ExtendWith(OutputCaptureExtension.class)
57-
@SuppressWarnings("removal")
5847
class WebFluxObservationAutoConfigurationTests {
5948

6049
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
6150
.with(MetricsRun.simple())
6251
.withConfiguration(
6352
AutoConfigurations.of(ObservationAutoConfiguration.class, WebFluxObservationAutoConfiguration.class));
6453

65-
@Test
66-
void shouldProvideWebFluxObservationFilter() {
67-
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ServerHttpObservationFilter.class));
68-
}
69-
70-
@Test
71-
void shouldProvideWebFluxObservationFilterOrdered() {
72-
this.contextRunner.withBean(FirstWebFilter.class).withBean(ThirdWebFilter.class).run((context) -> {
73-
List<WebFilter> webFilters = context.getBeanProvider(WebFilter.class).orderedStream().toList();
74-
assertThat(webFilters.get(0)).isInstanceOf(FirstWebFilter.class);
75-
assertThat(webFilters.get(1)).isInstanceOf(ServerHttpObservationFilter.class);
76-
assertThat(webFilters.get(2)).isInstanceOf(ThirdWebFilter.class);
77-
});
78-
}
79-
80-
@Test
81-
void shouldUseCustomConventionWhenAvailable() {
82-
this.contextRunner.withUserConfiguration(CustomConventionConfiguration.class).run((context) -> {
83-
assertThat(context).hasSingleBean(ServerHttpObservationFilter.class);
84-
assertThat(context).getBean(ServerHttpObservationFilter.class)
85-
.extracting("observationConvention")
86-
.isInstanceOf(CustomConvention.class);
87-
});
88-
}
89-
9054
@Test
9155
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
9256
this.contextRunner.withUserConfiguration(TestController.class)
@@ -108,7 +72,7 @@ void afterMaxUrisReachedFurtherUrisAreDeniedWhenUsingCustomObservationName(Captu
10872
.withPropertyValues("management.metrics.web.server.max-uri-tags=2",
10973
"management.observations.http.server.requests.name=my.http.server.requests")
11074
.run((context) -> {
111-
MeterRegistry registry = getInitializedMeterRegistry(context);
75+
MeterRegistry registry = getInitializedMeterRegistry(context, "my.http.server.requests");
11276
assertThat(registry.get("my.http.server.requests").meters()).hasSizeLessThanOrEqualTo(2);
11377
assertThat(output).contains("Reached the maximum number of URI tags for 'my.http.server.requests'");
11478
});
@@ -127,53 +91,17 @@ void shouldNotDenyNorLogIfMaxUrisIsNotReached(CapturedOutput output) {
12791
});
12892
}
12993

130-
private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context)
131-
throws Exception {
132-
return getInitializedMeterRegistry(context, "/test0", "/test1", "/test2");
94+
private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context) {
95+
return getInitializedMeterRegistry(context, "http.server.requests");
13396
}
13497

135-
private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context, String... urls)
136-
throws Exception {
137-
assertThat(context).hasSingleBean(ServerHttpObservationFilter.class);
138-
WebTestClient client = WebTestClient.bindToApplicationContext(context).build();
139-
for (String url : urls) {
140-
client.get().uri(url).exchange().expectStatus().isOk();
141-
}
142-
return context.getBean(MeterRegistry.class);
143-
}
144-
145-
@Configuration(proxyBeanMethods = false)
146-
static class CustomConventionConfiguration {
147-
148-
@Bean
149-
CustomConvention customConvention() {
150-
return new CustomConvention();
151-
}
152-
153-
}
154-
155-
static class CustomConvention extends DefaultServerRequestObservationConvention {
156-
157-
}
158-
159-
@Order(Ordered.HIGHEST_PRECEDENCE)
160-
static class FirstWebFilter implements WebFilter {
161-
162-
@Override
163-
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
164-
return chain.filter(exchange);
165-
}
166-
167-
}
168-
169-
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
170-
static class ThirdWebFilter implements WebFilter {
171-
172-
@Override
173-
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
174-
return chain.filter(exchange);
175-
}
176-
98+
private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context,
99+
String metricName) {
100+
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
101+
meterRegistry.timer(metricName, "uri", "/test0").record(Duration.of(500, ChronoUnit.SECONDS));
102+
meterRegistry.timer(metricName, "uri", "/test1").record(Duration.of(500, ChronoUnit.SECONDS));
103+
meterRegistry.timer(metricName, "uri", "/test2").record(Duration.of(500, ChronoUnit.SECONDS));
104+
return meterRegistry;
177105
}
178106

179107
}

spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc

-3
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,6 @@ When it does so, the orders shown in the following table will be used:
245245
|===
246246
| Web Filter | Order
247247

248-
| `ServerHttpObservationFilter` (Micrometer Observability)
249-
| `Ordered.HIGHEST_PRECEDENCE + 1`
250-
251248
| `WebFilterChainProxy` (Spring Security)
252249
| `-100`
253250

0 commit comments

Comments
 (0)