Skip to content

Commit 685fa90

Browse files
committed
Auto-configure Observation instrumentation for WebFlux
Prior to this commit, Spring Boot would offer a specific Metrics instrumentation for WebFlux applications through a `WebFilter` and custom Tag providers. As of Spring Framework 6.0, the Observation instrumentation is done directly in WebFlux, also with a `WebFilter`. While this allows both metrics and traces, some features cannot be supported in the same way with this new infrastructure. The former `WebFilter` has been removed and the Tagging infrastructure deprecated in favor of custom Observation conventions. This commit provides an adapter layer so that developers can refactor their custom tagging solution to the convention way, during the deprecation phase, without losing any feature. Closes gh-32539
1 parent cda63b5 commit 685fa90

File tree

19 files changed

+378
-704
lines changed

19 files changed

+378
-704
lines changed

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

-85
This file was deleted.

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

-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import io.micrometer.common.KeyValues;
2020
import io.micrometer.core.instrument.Tag;
21-
import io.micrometer.observation.Observation;
2221

2322
import org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTagsProvider;
2423
import org.springframework.http.client.observation.ClientRequestObservationContext;
@@ -42,11 +41,6 @@ class ClientHttpObservationConventionAdapter implements ClientRequestObservation
4241
this.tagsProvider = tagsProvider;
4342
}
4443

45-
@Override
46-
public boolean supportsContext(Observation.Context context) {
47-
return context instanceof ClientRequestObservationContext;
48-
}
49-
5044
@Override
5145
@SuppressWarnings("deprecation")
5246
public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation.web.reactive;
18+
19+
import java.util.List;
20+
21+
import io.micrometer.common.KeyValues;
22+
import io.micrometer.core.instrument.Tag;
23+
24+
import org.springframework.boot.actuate.metrics.web.reactive.server.DefaultWebFluxTagsProvider;
25+
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsContributor;
26+
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsProvider;
27+
import org.springframework.http.observation.reactive.ServerRequestObservationContext;
28+
import org.springframework.http.observation.reactive.ServerRequestObservationConvention;
29+
30+
/**
31+
* Adapter class that applies {@link WebFluxTagsProvider} tags as a
32+
* {@link ServerRequestObservationConvention}.
33+
*
34+
* @author Brian Clozel
35+
*/
36+
@SuppressWarnings("removal")
37+
class ServerRequestObservationConventionAdapter implements ServerRequestObservationConvention {
38+
39+
private final String name;
40+
41+
private final WebFluxTagsProvider tagsProvider;
42+
43+
ServerRequestObservationConventionAdapter(String name, WebFluxTagsProvider tagsProvider) {
44+
this.name = name;
45+
this.tagsProvider = tagsProvider;
46+
}
47+
48+
ServerRequestObservationConventionAdapter(String name, List<WebFluxTagsContributor> contributors) {
49+
this.name = name;
50+
this.tagsProvider = new DefaultWebFluxTagsProvider(contributors);
51+
}
52+
53+
@Override
54+
public String getName() {
55+
return this.name;
56+
}
57+
58+
@Override
59+
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
60+
KeyValues keyValues = KeyValues.empty();
61+
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(context.getServerWebExchange(), context.getError());
62+
for (Tag tag : tags) {
63+
keyValues = keyValues.and(tag.getKey(), tag.getValue());
64+
}
65+
return keyValues;
66+
}
67+
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation.web.reactive;
18+
19+
import java.util.List;
20+
21+
import io.micrometer.core.instrument.MeterRegistry;
22+
import io.micrometer.core.instrument.config.MeterFilter;
23+
import io.micrometer.observation.Observation;
24+
import io.micrometer.observation.ObservationRegistry;
25+
26+
import org.springframework.beans.factory.ObjectProvider;
27+
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
28+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
29+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
30+
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
31+
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
32+
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
33+
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
34+
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsContributor;
35+
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsProvider;
36+
import org.springframework.boot.autoconfigure.AutoConfiguration;
37+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
38+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
39+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
40+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
41+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
42+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
43+
import org.springframework.context.annotation.Bean;
44+
import org.springframework.context.annotation.Configuration;
45+
import org.springframework.core.annotation.Order;
46+
import org.springframework.http.observation.reactive.DefaultServerRequestObservationConvention;
47+
import org.springframework.http.observation.reactive.ServerRequestObservationConvention;
48+
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
49+
50+
/**
51+
* {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring
52+
* WebFlux applications.
53+
*
54+
* @author Brian Clozel
55+
* @author Jon Schneider
56+
* @author Dmytro Nosan
57+
* @since 3.0.0
58+
*/
59+
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
60+
SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class })
61+
@ConditionalOnClass(Observation.class)
62+
@ConditionalOnBean(ObservationRegistry.class)
63+
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
64+
@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class })
65+
@SuppressWarnings("removal")
66+
public class WebFluxObservationAutoConfiguration {
67+
68+
private final MetricsProperties metricsProperties;
69+
70+
private final ObservationProperties observationProperties;
71+
72+
public WebFluxObservationAutoConfiguration(MetricsProperties metricsProperties,
73+
ObservationProperties observationProperties) {
74+
this.metricsProperties = metricsProperties;
75+
this.observationProperties = observationProperties;
76+
}
77+
78+
@Bean
79+
@ConditionalOnMissingBean
80+
public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry,
81+
ObjectProvider<WebFluxTagsProvider> tagConfigurer,
82+
ObjectProvider<WebFluxTagsContributor> contributorsProvider) {
83+
84+
String observationName = this.observationProperties.getHttp().getServer().getRequests().getName();
85+
String metricName = this.metricsProperties.getWeb().getServer().getRequest().getMetricName();
86+
String name = (observationName != null) ? observationName : metricName;
87+
WebFluxTagsProvider tagsProvider = tagConfigurer.getIfAvailable();
88+
List<WebFluxTagsContributor> tagsContributors = contributorsProvider.orderedStream().toList();
89+
ServerRequestObservationConvention convention = new DefaultServerRequestObservationConvention(name);
90+
if (tagsProvider != null) {
91+
convention = new ServerRequestObservationConventionAdapter(name, tagsProvider);
92+
}
93+
else if (!tagsContributors.isEmpty()) {
94+
convention = new ServerRequestObservationConventionAdapter(name, tagsContributors);
95+
}
96+
return new ServerHttpObservationFilter(registry, convention);
97+
}
98+
99+
@Configuration(proxyBeanMethods = false)
100+
@ConditionalOnClass(MeterRegistry.class)
101+
@ConditionalOnBean(MeterRegistry.class)
102+
static class MeterFilterConfiguration {
103+
104+
@Bean
105+
@Order(0)
106+
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties properties) {
107+
String metricName = properties.getWeb().getServer().getRequest().getMetricName();
108+
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
109+
() -> String.format("Reached the maximum number of URI tags for '%s'.", metricName));
110+
return MeterFilter.maximumAllowableTags(metricName, "uri", properties.getWeb().getServer().getMaxUriTags(),
111+
filter);
112+
}
113+
114+
}
115+
116+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,6 @@
1515
*/
1616

1717
/**
18-
* Auto-configuration for WebFlux actuator metrics.
18+
* Auto-configuration for WebFlux actuator observations.
1919
*/
20-
package org.springframework.boot.actuate.autoconfigure.metrics.web.reactive;
20+
package org.springframework.boot.actuate.autoconfigure.observation.web.reactive;

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetric
7979
org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration
8080
org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration
8181
org.springframework.boot.actuate.autoconfigure.metrics.web.jetty.JettyMetricsAutoConfiguration
82-
org.springframework.boot.actuate.autoconfigure.metrics.web.reactive.WebFluxMetricsAutoConfiguration
82+
org.springframework.boot.actuate.autoconfigure.observation.web.reactive.WebFluxObservationAutoConfiguration
8383
org.springframework.boot.actuate.autoconfigure.metrics.web.tomcat.TomcatMetricsAutoConfiguration
8484
org.springframework.boot.actuate.autoconfigure.data.mongo.MongoHealthContributorAutoConfiguration
8585
org.springframework.boot.actuate.autoconfigure.data.mongo.MongoReactiveHealthContributorAutoConfiguration

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/test/MetricsIntegrationTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@
4141
import org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsAutoConfiguration;
4242
import org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration;
4343
import org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsAutoConfiguration;
44-
import org.springframework.boot.actuate.autoconfigure.metrics.web.reactive.WebFluxMetricsAutoConfiguration;
4544
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
4645
import org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration;
46+
import org.springframework.boot.actuate.autoconfigure.observation.web.reactive.WebFluxObservationAutoConfiguration;
4747
import org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration;
4848
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
4949
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
@@ -141,7 +141,7 @@ void metricsFilterRegisteredForAsyncDispatches() {
141141
SystemMetricsAutoConfiguration.class, RabbitMetricsAutoConfiguration.class,
142142
CacheMetricsAutoConfiguration.class, DataSourcePoolMetricsAutoConfiguration.class,
143143
HibernateMetricsAutoConfiguration.class, HttpClientObservationsAutoConfiguration.class,
144-
WebFluxMetricsAutoConfiguration.class, WebMvcObservationAutoConfiguration.class,
144+
WebFluxObservationAutoConfiguration.class, WebMvcObservationAutoConfiguration.class,
145145
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
146146
RestTemplateAutoConfiguration.class, WebMvcAutoConfiguration.class,
147147
DispatcherServletAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class })

0 commit comments

Comments
 (0)