Skip to content

Commit 385216a

Browse files
committed
Merge pull request spring-projects#14173 from nosan:spring-projectsgh-13913
* pr/14173: Polish "Limit metrics collection of incoming requests" Limit metrics collection of incoming requests
2 parents f9081a2 + 0625443 commit 385216a

File tree

10 files changed

+406
-75
lines changed

10 files changed

+406
-75
lines changed

Diff for: spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java

+15
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ public static class Server {
134134
*/
135135
private String requestsMetricName = "http.server.requests";
136136

137+
/**
138+
* Maximum number of unique URI tag values allowed. After the max number of
139+
* tag values is reached, metrics with additional tag values are denied by
140+
* filter.
141+
*/
142+
private int maxUriTags = 100;
143+
137144
public boolean isAutoTimeRequests() {
138145
return this.autoTimeRequests;
139146
}
@@ -150,6 +157,14 @@ public void setRequestsMetricName(String requestsMetricName) {
150157
this.requestsMetricName = requestsMetricName;
151158
}
152159

160+
public int getMaxUriTags() {
161+
return this.maxUriTags;
162+
}
163+
164+
public void setMaxUriTags(int maxUriTags) {
165+
this.maxUriTags = maxUriTags;
166+
}
167+
153168
}
154169

155170
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2012-2018 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+
* http://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.metrics;
18+
19+
import java.util.concurrent.atomic.AtomicBoolean;
20+
import java.util.function.Supplier;
21+
22+
import io.micrometer.core.instrument.Meter;
23+
import io.micrometer.core.instrument.config.MeterFilter;
24+
import io.micrometer.core.instrument.config.MeterFilterReply;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
28+
import org.springframework.util.Assert;
29+
30+
/**
31+
* {@link MeterFilter} to log only once a warning message and deny {@link Meter.Id}.
32+
*
33+
* @author Jon Schneider
34+
* @author Dmytro Nosan
35+
* @since 2.0.5
36+
*/
37+
public final class OnlyOnceLoggingDenyMeterFilter implements MeterFilter {
38+
39+
private final Logger logger = LoggerFactory
40+
.getLogger(OnlyOnceLoggingDenyMeterFilter.class);
41+
42+
private final AtomicBoolean alreadyWarned = new AtomicBoolean(false);
43+
44+
private final Supplier<String> message;
45+
46+
public OnlyOnceLoggingDenyMeterFilter(Supplier<String> message) {
47+
Assert.notNull(message, "Message must not be null");
48+
this.message = message;
49+
}
50+
51+
@Override
52+
public MeterFilterReply accept(Meter.Id id) {
53+
if (this.logger.isWarnEnabled()
54+
&& this.alreadyWarned.compareAndSet(false, true)) {
55+
this.logger.warn(this.message.get());
56+
}
57+
return MeterFilterReply.DENY;
58+
}
59+
60+
}

Diff for: spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsAutoConfiguration.java

+15-47
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,12 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.web.client;
1818

19-
import java.util.concurrent.atomic.AtomicBoolean;
20-
21-
import io.micrometer.core.instrument.Meter.Id;
2219
import io.micrometer.core.instrument.MeterRegistry;
2320
import io.micrometer.core.instrument.config.MeterFilter;
24-
import io.micrometer.core.instrument.config.MeterFilterReply;
25-
import org.slf4j.Logger;
26-
import org.slf4j.LoggerFactory;
2721

2822
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2923
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
24+
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
3025
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
3126
import org.springframework.boot.actuate.metrics.web.client.DefaultRestTemplateExchangeTagsProvider;
3227
import org.springframework.boot.actuate.metrics.web.client.MetricsRestTemplateCustomizer;
@@ -57,6 +52,12 @@
5752
@ConditionalOnBean(MeterRegistry.class)
5853
public class RestTemplateMetricsAutoConfiguration {
5954

55+
private final MetricsProperties properties;
56+
57+
public RestTemplateMetricsAutoConfiguration(MetricsProperties properties) {
58+
this.properties = properties;
59+
}
60+
6061
@Bean
6162
@ConditionalOnMissingBean(RestTemplateExchangeTagsProvider.class)
6263
public DefaultRestTemplateExchangeTagsProvider restTemplateTagConfigurer() {
@@ -66,53 +67,20 @@ public DefaultRestTemplateExchangeTagsProvider restTemplateTagConfigurer() {
6667
@Bean
6768
public MetricsRestTemplateCustomizer metricsRestTemplateCustomizer(
6869
MeterRegistry meterRegistry,
69-
RestTemplateExchangeTagsProvider restTemplateTagConfigurer,
70-
MetricsProperties properties) {
70+
RestTemplateExchangeTagsProvider restTemplateTagConfigurer) {
7171
return new MetricsRestTemplateCustomizer(meterRegistry, restTemplateTagConfigurer,
72-
properties.getWeb().getClient().getRequestsMetricName());
72+
this.properties.getWeb().getClient().getRequestsMetricName());
7373
}
7474

7575
@Bean
7676
@Order(0)
77-
public MeterFilter metricsWebClientUriTagFilter(MetricsProperties properties) {
78-
String metricName = properties.getWeb().getClient().getRequestsMetricName();
79-
MeterFilter denyFilter = new MaximumUriTagsReachedMeterFilter(metricName);
77+
public MeterFilter metricsWebClientUriTagFilter() {
78+
String metricName = this.properties.getWeb().getClient().getRequestsMetricName();
79+
MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter(() -> String
80+
.format("Reached the maximum number of URI tags for '%s'. Are you using "
81+
+ "'uriVariables' on RestTemplate calls?", metricName));
8082
return MeterFilter.maximumAllowableTags(metricName, "uri",
81-
properties.getWeb().getClient().getMaxUriTags(), denyFilter);
82-
}
83-
84-
/**
85-
* {@link MeterFilter} to deny further URI tags and log a warning.
86-
*/
87-
private static class MaximumUriTagsReachedMeterFilter implements MeterFilter {
88-
89-
private final Logger logger = LoggerFactory
90-
.getLogger(MaximumUriTagsReachedMeterFilter.class);
91-
92-
private final String metricName;
93-
94-
private final AtomicBoolean alreadyWarned = new AtomicBoolean(false);
95-
96-
MaximumUriTagsReachedMeterFilter(String metricName) {
97-
this.metricName = metricName;
98-
}
99-
100-
@Override
101-
public MeterFilterReply accept(Id id) {
102-
if (this.alreadyWarned.compareAndSet(false, true)) {
103-
logWarning();
104-
}
105-
return MeterFilterReply.DENY;
106-
}
107-
108-
private void logWarning() {
109-
if (this.logger.isWarnEnabled()) {
110-
this.logger.warn(
111-
"Reached the maximum number of URI tags for '" + this.metricName
112-
+ "'. Are you using uriVariables on RestTemplate calls?");
113-
}
114-
}
115-
83+
this.properties.getWeb().getClient().getMaxUriTags(), denyFilter);
11684
}
11785

11886
}

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

+22-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package org.springframework.boot.actuate.autoconfigure.metrics.web.reactive;
1818

1919
import io.micrometer.core.instrument.MeterRegistry;
20+
import io.micrometer.core.instrument.config.MeterFilter;
2021

2122
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2223
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
24+
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
2325
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
2426
import org.springframework.boot.actuate.metrics.web.reactive.server.DefaultWebFluxTagsProvider;
2527
import org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter;
@@ -31,12 +33,14 @@
3133
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
3234
import org.springframework.context.annotation.Bean;
3335
import org.springframework.context.annotation.Configuration;
36+
import org.springframework.core.annotation.Order;
3437

3538
/**
3639
* {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring
3740
* WebFlux MVC annotation-based programming model request mappings.
3841
*
3942
* @author Jon Schneider
43+
* @author Dmytro Nosan
4044
* @since 2.0.0
4145
*/
4246
@Configuration
@@ -46,6 +50,12 @@
4650
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
4751
public class WebFluxMetricsAutoConfiguration {
4852

53+
private final MetricsProperties properties;
54+
55+
public WebFluxMetricsAutoConfiguration(MetricsProperties properties) {
56+
this.properties = properties;
57+
}
58+
4959
@Bean
5060
@ConditionalOnMissingBean(WebFluxTagsProvider.class)
5161
public DefaultWebFluxTagsProvider webfluxTagConfigurer() {
@@ -54,9 +64,19 @@ public DefaultWebFluxTagsProvider webfluxTagConfigurer() {
5464

5565
@Bean
5666
public MetricsWebFilter webfluxMetrics(MeterRegistry registry,
57-
WebFluxTagsProvider tagConfigurer, MetricsProperties properties) {
67+
WebFluxTagsProvider tagConfigurer) {
5868
return new MetricsWebFilter(registry, tagConfigurer,
59-
properties.getWeb().getServer().getRequestsMetricName());
69+
this.properties.getWeb().getServer().getRequestsMetricName());
70+
}
71+
72+
@Bean
73+
@Order(0)
74+
public MeterFilter metricsHttpServerUriTagFilter() {
75+
String metricName = this.properties.getWeb().getServer().getRequestsMetricName();
76+
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(() -> String
77+
.format("Reached the maximum number of URI tags for '%s'.", metricName));
78+
return MeterFilter.maximumAllowableTags(metricName, "uri",
79+
this.properties.getWeb().getServer().getMaxUriTags(), filter);
6080
}
6181

6282
}

Diff for: spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.java

+23-3
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
import javax.servlet.DispatcherType;
2020

2121
import io.micrometer.core.instrument.MeterRegistry;
22+
import io.micrometer.core.instrument.config.MeterFilter;
2223

2324
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2425
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
2526
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server;
27+
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
2628
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
2729
import org.springframework.boot.actuate.metrics.web.servlet.DefaultWebMvcTagsProvider;
2830
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter;
@@ -38,6 +40,7 @@
3840
import org.springframework.context.annotation.Bean;
3941
import org.springframework.context.annotation.Configuration;
4042
import org.springframework.core.Ordered;
43+
import org.springframework.core.annotation.Order;
4144
import org.springframework.web.context.WebApplicationContext;
4245
import org.springframework.web.servlet.DispatcherServlet;
4346

@@ -46,6 +49,7 @@
4649
* MVC servlet-based request mappings.
4750
*
4851
* @author Jon Schneider
52+
* @author Dmytro Nosan
4953
* @since 2.0.0
5054
*/
5155
@Configuration
@@ -57,6 +61,12 @@
5761
@EnableConfigurationProperties(MetricsProperties.class)
5862
public class WebMvcMetricsAutoConfiguration {
5963

64+
private final MetricsProperties properties;
65+
66+
public WebMvcMetricsAutoConfiguration(MetricsProperties properties) {
67+
this.properties = properties;
68+
}
69+
6070
@Bean
6171
@ConditionalOnMissingBean(WebMvcTagsProvider.class)
6272
public DefaultWebMvcTagsProvider webMvcTagsProvider() {
@@ -65,9 +75,9 @@ public DefaultWebMvcTagsProvider webMvcTagsProvider() {
6575

6676
@Bean
6777
public FilterRegistrationBean<WebMvcMetricsFilter> webMvcMetricsFilter(
68-
MeterRegistry registry, MetricsProperties properties,
69-
WebMvcTagsProvider tagsProvider, WebApplicationContext context) {
70-
Server serverProperties = properties.getWeb().getServer();
78+
MeterRegistry registry, WebMvcTagsProvider tagsProvider,
79+
WebApplicationContext context) {
80+
Server serverProperties = this.properties.getWeb().getServer();
7181
WebMvcMetricsFilter filter = new WebMvcMetricsFilter(context, registry,
7282
tagsProvider, serverProperties.getRequestsMetricName(),
7383
serverProperties.isAutoTimeRequests());
@@ -78,4 +88,14 @@ public FilterRegistrationBean<WebMvcMetricsFilter> webMvcMetricsFilter(
7888
return registration;
7989
}
8090

91+
@Bean
92+
@Order(0)
93+
public MeterFilter metricsHttpServerUriTagFilter() {
94+
String metricName = this.properties.getWeb().getServer().getRequestsMetricName();
95+
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(() -> String
96+
.format("Reached the maximum number of URI tags for '%s'.", metricName));
97+
return MeterFilter.maximumAllowableTags(metricName, "uri",
98+
this.properties.getWeb().getServer().getMaxUriTags(), filter);
99+
}
100+
81101
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2018 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+
* http://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.metrics.web;
18+
19+
import org.springframework.web.bind.annotation.GetMapping;
20+
import org.springframework.web.bind.annotation.RestController;
21+
22+
/**
23+
* Test controller used by metrics tests.
24+
*
25+
* @author Dmytro Nosan
26+
* @author Stephane Nicoll
27+
*/
28+
@RestController
29+
public class TestController {
30+
31+
@GetMapping("test0")
32+
public String test0() {
33+
return "test0";
34+
}
35+
36+
@GetMapping("test1")
37+
public String test1() {
38+
return "test1";
39+
}
40+
41+
@GetMapping("test2")
42+
public String test2() {
43+
return "test2";
44+
}
45+
46+
}

0 commit comments

Comments
 (0)