Skip to content

Commit e24f26e

Browse files
committed
Add properties for default config of auto-timed controller metrics
When `management.metrics.web.server.autoTimeRequests` is enabled (default=true), spring-boot collects metrics on controller methods even when they are not annotated with `@Timed`. When this happens, created metrics are based on the default configuration values of `@Timed`. Currently, there is no way to specify the default configuration to those auto-timed controller metrics. This commit introduces two properties: - `management.metrics.web.server.autoTimeRequestsDefaultPercentiles` - `management.metrics.web.server.autoTimeRequestsDefaultHistogram` When values are set to the above properties, they will be applied to auto-timed controller metrics.
1 parent c8e46f6 commit e24f26e

File tree

10 files changed

+165
-29
lines changed

10 files changed

+165
-29
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.LinkedHashMap;
2020
import java.util.Map;
2121

22+
import io.micrometer.core.annotation.Timed;
23+
2224
import org.springframework.boot.context.properties.ConfigurationProperties;
2325

2426
/**
@@ -133,6 +135,20 @@ public static class Server {
133135
*/
134136
private boolean autoTimeRequests = true;
135137

138+
/**
139+
* Default percentiles when "autoTimeRequests=true" and @Timed annotation is
140+
* not presented on the corresponding request handler. Any @Timed annotation
141+
* presented will have precedence.
142+
*/
143+
private double[] autoTimeRequestsDefaultPercentiles;
144+
145+
/**
146+
* Default histogram when "autoTimeRequests=true" and @Timed annotation is not
147+
* presented on the corresponding request handler. Any @Timed annotation
148+
* presented will have precedence.
149+
*/
150+
private boolean autoTimeRequestsDefaultHistogram;
151+
136152
/**
137153
* Name of the metric for received requests.
138154
*/
@@ -153,6 +169,24 @@ public void setAutoTimeRequests(boolean autoTimeRequests) {
153169
this.autoTimeRequests = autoTimeRequests;
154170
}
155171

172+
public double[] getAutoTimeRequestsDefaultPercentiles() {
173+
return this.autoTimeRequestsDefaultPercentiles;
174+
}
175+
176+
public void setAutoTimeRequestsDefaultPercentiles(
177+
double[] autoTimeRequestsDefaultPercentiles) {
178+
this.autoTimeRequestsDefaultPercentiles = autoTimeRequestsDefaultPercentiles;
179+
}
180+
181+
public boolean isAutoTimeRequestsDefaultHistogram() {
182+
return this.autoTimeRequestsDefaultHistogram;
183+
}
184+
185+
public void setAutoTimeRequestsDefaultHistogram(
186+
boolean autoTimeRequestsDefaultHistogram) {
187+
this.autoTimeRequestsDefaultHistogram = autoTimeRequestsDefaultHistogram;
188+
}
189+
156190
public String getRequestsMetricName() {
157191
return this.requestsMetricName;
158192
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2323
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
24+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server;
2425
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
2526
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
2627
import org.springframework.boot.actuate.metrics.web.reactive.server.DefaultWebFluxTagsProvider;
@@ -65,9 +66,12 @@ public DefaultWebFluxTagsProvider webfluxTagConfigurer() {
6566
@Bean
6667
public MetricsWebFilter webfluxMetrics(MeterRegistry registry,
6768
WebFluxTagsProvider tagConfigurer) {
69+
Server serverProperties = this.properties.getWeb().getServer();
6870
return new MetricsWebFilter(registry, tagConfigurer,
69-
this.properties.getWeb().getServer().getRequestsMetricName(),
70-
this.properties.getWeb().getServer().isAutoTimeRequests());
71+
serverProperties.getRequestsMetricName(),
72+
serverProperties.isAutoTimeRequests(),
73+
serverProperties.getAutoTimeRequestsDefaultPercentiles(),
74+
serverProperties.isAutoTimeRequestsDefaultHistogram());
7175
}
7276

7377
@Bean

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ public FilterRegistrationBean<WebMvcMetricsFilter> webMvcMetricsFilter(
8181
Server serverProperties = this.properties.getWeb().getServer();
8282
WebMvcMetricsFilter filter = new WebMvcMetricsFilter(registry, tagsProvider,
8383
serverProperties.getRequestsMetricName(),
84-
serverProperties.isAutoTimeRequests());
84+
serverProperties.isAutoTimeRequests(),
85+
serverProperties.getAutoTimeRequestsDefaultPercentiles(),
86+
serverProperties.isAutoTimeRequestsDefaultHistogram());
8587
FilterRegistrationBean<WebMvcMetricsFilter> registration = new FilterRegistrationBean<>(
8688
filter);
8789
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfigurationTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
import io.micrometer.core.instrument.MeterRegistry;
2828
import io.micrometer.core.instrument.Tag;
29+
import io.micrometer.core.instrument.Timer;
30+
import io.micrometer.core.instrument.distribution.HistogramSnapshot;
2931
import org.junit.Rule;
3032
import org.junit.Test;
3133

@@ -137,6 +139,26 @@ public void shouldNotDenyNorLogIfMaxUrisIsNotReached() {
137139
});
138140
}
139141

142+
@Test
143+
public void autoTimeRequestsDefaultValues() {
144+
this.contextRunner.withUserConfiguration(TestController.class)
145+
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class,
146+
WebMvcAutoConfiguration.class))
147+
.withPropertyValues(
148+
"management.metrics.web.server.autoTimeRequestsDefaultPercentiles=0.5,0.7",
149+
"management.metrics.web.server.autoTimeRequestsDefaultHistogram=true")
150+
.run((context) -> {
151+
MeterRegistry registry = getInitializedMeterRegistry(context);
152+
Timer timer = registry.get("http.server.requests").timer();
153+
HistogramSnapshot snapshot = timer.takeSnapshot();
154+
assertThat(snapshot.percentileValues()).hasSize(2);
155+
assertThat(snapshot.percentileValues()[0].percentile())
156+
.isEqualTo(0.5);
157+
assertThat(snapshot.percentileValues()[1].percentile())
158+
.isEqualTo(0.7);
159+
});
160+
}
161+
140162
@Test
141163
@SuppressWarnings("rawtypes")
142164
public void longTaskTimingInterceptorIsRegistered() {

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

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import io.micrometer.core.instrument.MeterRegistry;
2222
import io.micrometer.core.instrument.Tag;
23+
import io.micrometer.core.instrument.Timer;
2324
import org.reactivestreams.Publisher;
2425
import reactor.core.publisher.Mono;
2526

@@ -48,12 +49,46 @@ public class MetricsWebFilter implements WebFilter {
4849

4950
private final boolean autoTimeRequests;
5051

52+
private final double[] autoTimeRequestsDefaultPercentiles;
53+
54+
private final boolean autoTimeRequestsDefaultHistogram;
55+
56+
/**
57+
* Create a new {@code MetricsWebFilter}.
58+
* @param registry the registry to which metrics are recorded
59+
* @param tagsProvider provider for metrics tags
60+
* @param metricName name of the metric to record
61+
* @param autoTimeRequests if requests should be automatically timed
62+
* @deprecated since 2.1.4 in favor of
63+
* {@link #MetricsWebFilter(MeterRegistry, WebFluxTagsProvider, String, boolean, double[], boolean)}
64+
*/
65+
@Deprecated
5166
public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
5267
String metricName, boolean autoTimeRequests) {
68+
this(registry, tagsProvider, metricName, autoTimeRequests, null, false);
69+
}
70+
71+
/**
72+
* Create a new {@code MetricsWebFilter}.
73+
* @param registry the registry to which metrics are recorded
74+
* @param tagsProvider provider for metrics tags
75+
* @param metricName name of the metric to record
76+
* @param autoTimeRequests if requests should be automatically timed
77+
* @param autoTimeRequestsDefaultPercentiles default percentiles for auto time
78+
* requests
79+
* @param autoTimeRequestsDefaultHistogram default histogram for auto time requests
80+
* @since 2.1.4
81+
*/
82+
public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
83+
String metricName, boolean autoTimeRequests,
84+
double[] autoTimeRequestsDefaultPercentiles,
85+
boolean autoTimeRequestsDefaultHistogram) {
5386
this.registry = registry;
5487
this.tagsProvider = tagsProvider;
5588
this.metricName = metricName;
5689
this.autoTimeRequests = autoTimeRequests;
90+
this.autoTimeRequestsDefaultPercentiles = autoTimeRequestsDefaultPercentiles;
91+
this.autoTimeRequestsDefaultHistogram = autoTimeRequestsDefaultHistogram;
5792
}
5893

5994
@Override
@@ -67,29 +102,27 @@ public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
67102
private Publisher<Void> filter(ServerWebExchange exchange, Mono<Void> call) {
68103
long start = System.nanoTime();
69104
ServerHttpResponse response = exchange.getResponse();
70-
return call.doOnSuccess((done) -> success(exchange, start)).doOnError((cause) -> {
71-
if (response.isCommitted()) {
72-
error(exchange, start, cause);
73-
}
74-
else {
75-
response.beforeCommit(() -> {
76-
error(exchange, start, cause);
77-
return Mono.empty();
105+
return call.doOnSuccess((done) -> record(exchange, start, null))
106+
.doOnError((cause) -> {
107+
if (response.isCommitted()) {
108+
record(exchange, start, cause);
109+
}
110+
else {
111+
response.beforeCommit(() -> {
112+
record(exchange, start, cause);
113+
return Mono.empty();
114+
});
115+
}
78116
});
79-
}
80-
});
81-
}
82-
83-
private void success(ServerWebExchange exchange, long start) {
84-
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, null);
85-
this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
86-
TimeUnit.NANOSECONDS);
87117
}
88118

89-
private void error(ServerWebExchange exchange, long start, Throwable cause) {
119+
private void record(ServerWebExchange exchange, long start, Throwable cause) {
90120
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, cause);
91-
this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
92-
TimeUnit.NANOSECONDS);
121+
Timer.builder(this.metricName).tags(tags)
122+
.publishPercentiles(this.autoTimeRequestsDefaultPercentiles)
123+
.publishPercentileHistogram(this.autoTimeRequestsDefaultHistogram)
124+
.register(this.registry)
125+
.record(System.nanoTime() - start, TimeUnit.NANOSECONDS);
93126
}
94127

95128
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,48 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
6060

6161
private final boolean autoTimeRequests;
6262

63+
private final double[] autoTimeRequestsDefaultPercentiles;
64+
65+
private final boolean autoTimeRequestsDefaultHistogram;
66+
6367
/**
6468
* Create a new {@link WebMvcMetricsFilter} instance.
6569
* @param registry the meter registry
6670
* @param tagsProvider the tags provider
6771
* @param metricName the metric name
6872
* @param autoTimeRequests if requests should be automatically timed
6973
* @since 2.0.7
74+
* @deprecated since 2.1.4 in favor of
75+
* {@link #WebMvcMetricsFilter(MeterRegistry, WebMvcTagsProvider, String, boolean, double[], boolean)}
7076
*/
77+
@Deprecated
7178
public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider,
7279
String metricName, boolean autoTimeRequests) {
80+
this(registry, tagsProvider, metricName, autoTimeRequests, null, false);
81+
}
82+
83+
/**
84+
* Create a new {@link WebMvcMetricsFilter} instance.
85+
* @param registry the meter registry
86+
* @param tagsProvider the tags provider
87+
* @param metricName the metric name
88+
* @param autoTimeRequests if requests should be automatically timed
89+
* @param autoTimeRequestsDefaultPercentiles default percentiles if requests are auto
90+
* timed
91+
* @param autoTimeRequestsDefaultHistogram default histogram flag if requests are auto
92+
* timed
93+
* @since 2.1.4
94+
*/
95+
public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider,
96+
String metricName, boolean autoTimeRequests,
97+
double[] autoTimeRequestsDefaultPercentiles,
98+
boolean autoTimeRequestsDefaultHistogram) {
7399
this.registry = registry;
74100
this.tagsProvider = tagsProvider;
75101
this.metricName = metricName;
76102
this.autoTimeRequests = autoTimeRequests;
103+
this.autoTimeRequestsDefaultPercentiles = autoTimeRequestsDefaultPercentiles;
104+
this.autoTimeRequestsDefaultHistogram = autoTimeRequestsDefaultHistogram;
77105
}
78106

79107
@Override
@@ -150,7 +178,12 @@ private void record(TimingContext timingContext, HttpServletResponse response,
150178
handlerObject, exception);
151179
if (annotations.isEmpty()) {
152180
if (this.autoTimeRequests) {
153-
stop(timerSample, tags, Timer.builder(this.metricName));
181+
stop(timerSample, tags,
182+
Timer.builder(this.metricName)
183+
.publishPercentiles(
184+
this.autoTimeRequestsDefaultPercentiles)
185+
.publishPercentileHistogram(
186+
this.autoTimeRequestsDefaultHistogram));
154187
}
155188
}
156189
else {

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilterTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public void setup() {
5050
MockClock clock = new MockClock();
5151
this.registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, clock);
5252
this.webFilter = new MetricsWebFilter(this.registry,
53-
new DefaultWebFluxTagsProvider(), REQUEST_METRICS_NAME, true);
53+
new DefaultWebFluxTagsProvider(), REQUEST_METRICS_NAME, true, null,
54+
false);
5455
}
5556

5657
@Test

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterAutoTimedTests.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import io.micrometer.core.instrument.Clock;
2020
import io.micrometer.core.instrument.MeterRegistry;
2121
import io.micrometer.core.instrument.MockClock;
22+
import io.micrometer.core.instrument.Timer;
23+
import io.micrometer.core.instrument.distribution.HistogramSnapshot;
2224
import io.micrometer.core.instrument.simple.SimpleConfig;
2325
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
2426
import org.junit.Before;
@@ -73,8 +75,13 @@ public void setupMockMvc() {
7375
@Test
7476
public void metricsCanBeAutoTimed() throws Exception {
7577
this.mvc.perform(get("/api/10")).andExpect(status().isOk());
76-
assertThat(this.registry.get("http.server.requests").tags("status", "200").timer()
77-
.count()).isEqualTo(1L);
78+
Timer timer = this.registry.get("http.server.requests").tags("status", "200")
79+
.timer();
80+
assertThat(timer.count()).isEqualTo(1L);
81+
HistogramSnapshot snapshot = timer.takeSnapshot();
82+
assertThat(snapshot.percentileValues()).hasSize(2);
83+
assertThat(snapshot.percentileValues()[0].percentile()).isEqualTo(0.5);
84+
assertThat(snapshot.percentileValues()[1].percentile()).isEqualTo(0.95);
7885
}
7986

8087
@Configuration
@@ -96,7 +103,7 @@ MeterRegistry meterRegistry(Clock clock) {
96103
public WebMvcMetricsFilter webMetricsFilter(WebApplicationContext context,
97104
MeterRegistry registry) {
98105
return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(),
99-
"http.server.requests", true);
106+
"http.server.requests", true, new double[] { 0.5, 0.95 }, true);
100107
}
101108

102109
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ CyclicBarrier completableFutureBarrier() {
361361
WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry,
362362
WebApplicationContext ctx) {
363363
return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(),
364-
"http.server.requests", true);
364+
"http.server.requests", true, null, false);
365365
}
366366

367367
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ MeterRegistry meterRegistry(Clock clock) {
111111
public WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry,
112112
WebApplicationContext ctx) {
113113
return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(),
114-
"http.server.requests", true);
114+
"http.server.requests", true, null, false);
115115
}
116116

117117
@RestController

0 commit comments

Comments
 (0)