Skip to content

Commit dac7aa6

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 9540905 commit dac7aa6

File tree

7 files changed

+137
-26
lines changed

7 files changed

+137
-26
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 {@link #autoTimeRequests} is {@code true} and
140+
* {@link Timed} is not presented on the corresponding request handler. Any
141+
* {@link Timed} annotation presented will have precedence.
142+
*/
143+
private double[] autoTimeRequestsDefaultPercentiles;
144+
145+
/**
146+
* Default histogram when {@link #autoTimeRequests} is {@code true} and
147+
* {@link Timed} is not presented on the corresponding request handler. Any
148+
* {@link Timed} annotation 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: 31 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,6 +49,10 @@ public class MetricsWebFilter implements WebFilter {
4849

4950
private final boolean autoTimeRequests;
5051

52+
private final double[] autoTimeRequestsDefaultPercentiles;
53+
54+
private final boolean autoTimeRequestsDefaultHistogram;
55+
5156
/**
5257
* Create a new {@code MetricsWebFilter}.
5358
* @param registry the registry to which metrics are recorded
@@ -64,10 +69,19 @@ public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider
6469

6570
public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
6671
String metricName, boolean autoTimeRequests) {
72+
this(registry, tagsProvider, metricName, autoTimeRequests, null, false);
73+
}
74+
75+
public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
76+
String metricName, boolean autoTimeRequests,
77+
double[] autoTimeRequestsDefaultPercentiles,
78+
boolean autoTimeRequestsDefaultHistogram) {
6779
this.registry = registry;
6880
this.tagsProvider = tagsProvider;
6981
this.metricName = metricName;
7082
this.autoTimeRequests = autoTimeRequests;
83+
this.autoTimeRequestsDefaultPercentiles = autoTimeRequestsDefaultPercentiles;
84+
this.autoTimeRequestsDefaultHistogram = autoTimeRequestsDefaultHistogram;
7185
}
7286

7387
@Override
@@ -81,29 +95,27 @@ public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
8195
private Publisher<Void> filter(ServerWebExchange exchange, Mono<Void> call) {
8296
long start = System.nanoTime();
8397
ServerHttpResponse response = exchange.getResponse();
84-
return call.doOnSuccess((done) -> success(exchange, start)).doOnError((cause) -> {
85-
if (response.isCommitted()) {
86-
error(exchange, start, cause);
87-
}
88-
else {
89-
response.beforeCommit(() -> {
90-
error(exchange, start, cause);
91-
return Mono.empty();
98+
return call.doOnSuccess((done) -> record(exchange, start, null))
99+
.doOnError((cause) -> {
100+
if (response.isCommitted()) {
101+
record(exchange, start, cause);
102+
}
103+
else {
104+
response.beforeCommit(() -> {
105+
record(exchange, start, cause);
106+
return Mono.empty();
107+
});
108+
}
92109
});
93-
}
94-
});
95-
}
96-
97-
private void success(ServerWebExchange exchange, long start) {
98-
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, null);
99-
this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
100-
TimeUnit.NANOSECONDS);
101110
}
102111

103-
private void error(ServerWebExchange exchange, long start, Throwable cause) {
112+
private void record(ServerWebExchange exchange, long start, Throwable cause) {
104113
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, cause);
105-
this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
106-
TimeUnit.NANOSECONDS);
114+
Timer.builder(this.metricName).tags(tags)
115+
.publishPercentiles(this.autoTimeRequestsDefaultPercentiles)
116+
.publishPercentileHistogram(this.autoTimeRequestsDefaultHistogram)
117+
.register(this.registry)
118+
.record(System.nanoTime() - start, TimeUnit.NANOSECONDS);
107119
}
108120

109121
}

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
6161

6262
private final boolean autoTimeRequests;
6363

64+
private final double[] autoTimeRequestsDefaultPercentiles;
65+
66+
private final boolean autoTimeRequestsDefaultHistogram;
67+
6468
/**
6569
* Create a new {@link WebMvcMetricsFilter} instance.
6670
* @param context the source application context
@@ -88,10 +92,31 @@ public WebMvcMetricsFilter(ApplicationContext context, MeterRegistry registry,
8892
*/
8993
public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider,
9094
String metricName, boolean autoTimeRequests) {
95+
this(registry, tagsProvider, metricName, autoTimeRequests, null, false);
96+
}
97+
98+
/**
99+
* Create a new {@link WebMvcMetricsFilter} instance.
100+
* @param registry the meter registry
101+
* @param tagsProvider the tags provider
102+
* @param metricName the metric name
103+
* @param autoTimeRequests if requests should be automatically timed
104+
* @param autoTimeRequestsDefaultPercentiles default percentiles if requests are auto
105+
* timed
106+
* @param autoTimeRequestsDefaultHistogram default histogram flag if requests are auto
107+
* timed
108+
* @since 2.1.4
109+
*/
110+
public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider,
111+
String metricName, boolean autoTimeRequests,
112+
double[] autoTimeRequestsDefaultPercentiles,
113+
boolean autoTimeRequestsDefaultHistogram) {
91114
this.registry = registry;
92115
this.tagsProvider = tagsProvider;
93116
this.metricName = metricName;
94117
this.autoTimeRequests = autoTimeRequests;
118+
this.autoTimeRequestsDefaultPercentiles = autoTimeRequestsDefaultPercentiles;
119+
this.autoTimeRequestsDefaultHistogram = autoTimeRequestsDefaultHistogram;
95120
}
96121

97122
@Override
@@ -168,7 +193,12 @@ private void record(TimingContext timingContext, HttpServletResponse response,
168193
handlerObject, exception);
169194
if (annotations.isEmpty()) {
170195
if (this.autoTimeRequests) {
171-
stop(timerSample, tags, Timer.builder(this.metricName));
196+
stop(timerSample, tags,
197+
Timer.builder(this.metricName)
198+
.publishPercentiles(
199+
this.autoTimeRequestsDefaultPercentiles)
200+
.publishPercentileHistogram(
201+
this.autoTimeRequestsDefaultHistogram));
172202
}
173203
}
174204
else {

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
}

0 commit comments

Comments
 (0)