diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java index 2bca5a6036..ec9a83758a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java @@ -19,7 +19,10 @@ package org.springframework.boot.actuate.autoconfigure.metrics; import java.util.LinkedHashMap; import java.util.Map; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; +import org.springframework.boot.context.properties.NestedConfigurationProperty; /** * {@link ConfigurationProperties @ConfigurationProperties} for configuring @@ -27,6 +30,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * * @author Jon Schneider * @author Alexander Abramov + * @author Tadaya Tsuyukubo * @since 2.0.0 */ @ConfigurationProperties("management.metrics") @@ -94,10 +98,7 @@ public class MetricsProperties { public static class Client { - /** - * Name of the metric for sent requests. - */ - private String requestsMetricName = "http.client.requests"; + private final ClientRequest request = new ClientRequest(); /** * Maximum number of unique URI tag values allowed. After the max number of @@ -106,12 +107,29 @@ public class MetricsProperties { */ private int maxUriTags = 100; - public String getRequestsMetricName() { - return this.requestsMetricName; + public ClientRequest getRequest() { + return this.request; } + /** + * Return the name of the metric for client requests. + * @return request metric name + * @deprecated since 2.2.0 in favor of {@link ClientRequest#getMetricName()} + */ + @DeprecatedConfigurationProperty( + replacement = "management.metrics.web.client.request.metric-name") + public String getRequestsMetricName() { + return this.request.getMetricName(); + } + + /** + * Set the name of the metric for client requests. + * @param requestsMetricName request metric name + * @deprecated since 2.2.0 in favor of + * {@link ClientRequest#setMetricName(String)} + */ public void setRequestsMetricName(String requestsMetricName) { - this.requestsMetricName = requestsMetricName; + this.request.setMetricName(requestsMetricName); } public int getMaxUriTags() { @@ -122,22 +140,38 @@ public class MetricsProperties { this.maxUriTags = maxUriTags; } + public static class ClientRequest { + + /** + * Name of the metric for sent requests. + */ + private String metricName = "http.client.requests"; + + /** + * Auto-timed request settings. + */ + @NestedConfigurationProperty + private final Autotime autoTime = new Autotime(); + + public Autotime getAutotime() { + return this.autoTime; + } + + public String getMetricName() { + return this.metricName; + } + + public void setMetricName(String metricName) { + this.metricName = metricName; + } + + } + } public static class Server { - /** - * Whether requests handled by Spring MVC, WebFlux or Jersey should be - * automatically timed. If the number of time series emitted grows too large - * on account of request mapping timings, disable this and use 'Timed' on a - * per request mapping basis as needed. - */ - private boolean autoTimeRequests = true; - - /** - * Name of the metric for received requests. - */ - private String requestsMetricName = "http.server.requests"; + private final ServerRequest request = new ServerRequest(); /** * Maximum number of unique URI tag values allowed. After the max number of @@ -146,20 +180,29 @@ public class MetricsProperties { */ private int maxUriTags = 100; - public boolean isAutoTimeRequests() { - return this.autoTimeRequests; - } - - public void setAutoTimeRequests(boolean autoTimeRequests) { - this.autoTimeRequests = autoTimeRequests; + public ServerRequest getRequest() { + return this.request; } + /** + * Return name of the metric for server requests. + * @return request metric name + * @deprecated since 2.2.0 in favor of {@link ServerRequest#getMetricName()} + */ + @DeprecatedConfigurationProperty( + replacement = "management.metrics.web.server.request.metric-name") public String getRequestsMetricName() { - return this.requestsMetricName; + return this.request.getMetricName(); } + /** + * Set the name of the metric for server requests. + * @param requestsMetricName request metric name + * @deprecated since 2.2.0 in favor of + * {@link ServerRequest#setMetricName(String)} + */ public void setRequestsMetricName(String requestsMetricName) { - this.requestsMetricName = requestsMetricName; + this.request.setMetricName(requestsMetricName); } public int getMaxUriTags() { @@ -170,6 +213,33 @@ public class MetricsProperties { this.maxUriTags = maxUriTags; } + public static class ServerRequest { + + /** + * Name of the metric for received requests. + */ + private String metricName = "http.server.requests"; + + /** + * Auto-timed request settings. + */ + @NestedConfigurationProperty + private final Autotime autotime = new Autotime(); + + public Autotime getAutotime() { + return this.autotime; + } + + public String getMetricName() { + return this.metricName; + } + + public void setMetricName(String metricName) { + this.metricName = metricName; + } + + } + } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java index ec10ae311f..d657e0a5d0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java @@ -79,14 +79,16 @@ public class JerseyServerMetricsAutoConfiguration { MeterRegistry meterRegistry, JerseyTagsProvider tagsProvider) { Server server = this.properties.getWeb().getServer(); return (config) -> config.register(new MetricsApplicationEventListener( - meterRegistry, tagsProvider, server.getRequestsMetricName(), - server.isAutoTimeRequests(), new AnnotationUtilsAnnotationFinder())); + meterRegistry, tagsProvider, server.getRequest().getMetricName(), + server.getRequest().getAutotime().isEnabled(), + new AnnotationUtilsAnnotationFinder())); } @Bean @Order(0) public MeterFilter jerseyMetricsUriTagFilter() { - String metricName = this.properties.getWeb().getServer().getRequestsMetricName(); + String metricName = this.properties.getWeb().getServer().getRequest() + .getMetricName(); MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(() -> String .format("Reached the maximum number of URI tags for '%s'.", metricName)); return MeterFilter.maximumAllowableTags(metricName, "uri", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/HttpClientMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/HttpClientMetricsAutoConfiguration.java index def5e7174a..6fdb0a8ad0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/HttpClientMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/HttpClientMetricsAutoConfiguration.java @@ -53,7 +53,7 @@ public class HttpClientMetricsAutoConfiguration { @Bean @Order(0) public MeterFilter metricsHttpClientUriTagFilter(MetricsProperties properties) { - String metricName = properties.getWeb().getClient().getRequestsMetricName(); + String metricName = properties.getWeb().getClient().getRequest().getMetricName(); MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter(() -> String .format("Reached the maximum number of URI tags for '%s'. Are you using " + "'uriVariables'?", metricName)); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.java index 6ec64b51a3..037fe32b8f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.metrics.web.client; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Client.ClientRequest; import org.springframework.boot.actuate.metrics.web.client.DefaultRestTemplateExchangeTagsProvider; import org.springframework.boot.actuate.metrics.web.client.MetricsRestTemplateCustomizer; import org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTagsProvider; @@ -53,9 +54,10 @@ class RestTemplateMetricsConfiguration { MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider restTemplateExchangeTagsProvider, MetricsProperties properties) { + ClientRequest request = properties.getWeb().getClient().getRequest(); return new MetricsRestTemplateCustomizer(meterRegistry, - restTemplateExchangeTagsProvider, - properties.getWeb().getClient().getRequestsMetricName()); + restTemplateExchangeTagsProvider, request.getMetricName(), + request.getAutotime()); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java index dbbf4e0628..689772f0e0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.metrics.web.client; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Client.ClientRequest; import org.springframework.boot.actuate.metrics.web.reactive.client.DefaultWebClientExchangeTagsProvider; import org.springframework.boot.actuate.metrics.web.reactive.client.MetricsWebClientCustomizer; import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider; @@ -48,8 +49,9 @@ class WebClientMetricsConfiguration { public MetricsWebClientCustomizer metricsWebClientCustomizer( MeterRegistry meterRegistry, WebClientExchangeTagsProvider tagsProvider, MetricsProperties properties) { + ClientRequest request = properties.getWeb().getClient().getRequest(); return new MetricsWebClientCustomizer(meterRegistry, tagsProvider, - properties.getWeb().getClient().getRequestsMetricName()); + request.getMetricName(), request.getAutotime()); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfiguration.java index 87280c5448..fc9327ee32 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfiguration.java @@ -21,6 +21,7 @@ import io.micrometer.core.instrument.config.MeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server.ServerRequest; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.actuate.metrics.web.reactive.server.DefaultWebFluxTagsProvider; @@ -65,15 +66,16 @@ public class WebFluxMetricsAutoConfiguration { @Bean public MetricsWebFilter webfluxMetrics(MeterRegistry registry, WebFluxTagsProvider tagConfigurer) { - return new MetricsWebFilter(registry, tagConfigurer, - this.properties.getWeb().getServer().getRequestsMetricName(), - this.properties.getWeb().getServer().isAutoTimeRequests()); + ServerRequest request = this.properties.getWeb().getServer().getRequest(); + return new MetricsWebFilter(registry, tagConfigurer, request.getMetricName(), + request.getAutotime()); } @Bean @Order(0) public MeterFilter metricsHttpServerUriTagFilter() { - String metricName = this.properties.getWeb().getServer().getRequestsMetricName(); + String metricName = this.properties.getWeb().getServer().getRequest() + .getMetricName(); MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(() -> String .format("Reached the maximum number of URI tags for '%s'.", metricName)); return MeterFilter.maximumAllowableTags(metricName, "uri", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.java index ffa95accfe..7f20381ead 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.java @@ -23,7 +23,7 @@ import io.micrometer.core.instrument.config.MeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server.ServerRequest; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.actuate.metrics.web.servlet.DefaultWebMvcTagsProvider; @@ -78,10 +78,9 @@ public class WebMvcMetricsAutoConfiguration { @Bean public FilterRegistrationBean webMvcMetricsFilter( MeterRegistry registry, WebMvcTagsProvider tagsProvider) { - Server serverProperties = this.properties.getWeb().getServer(); + ServerRequest request = this.properties.getWeb().getServer().getRequest(); WebMvcMetricsFilter filter = new WebMvcMetricsFilter(registry, tagsProvider, - serverProperties.getRequestsMetricName(), - serverProperties.isAutoTimeRequests()); + request.getMetricName(), request.getAutotime()); FilterRegistrationBean registration = new FilterRegistrationBean<>( filter); registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); @@ -92,7 +91,8 @@ public class WebMvcMetricsAutoConfiguration { @Bean @Order(0) public MeterFilter metricsHttpServerUriTagFilter() { - String metricName = this.properties.getWeb().getServer().getRequestsMetricName(); + String metricName = this.properties.getWeb().getServer().getRequest() + .getMetricName(); MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(() -> String .format("Reached the maximum number of URI tags for '%s'.", metricName)); return MeterFilter.maximumAllowableTags(metricName, "uri", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index f853bd532c..08a7529ec5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -253,6 +253,34 @@ "level": "error" } }, + { + "name": "management.metrics.web.client.request.autotime.enabled", + "description": "Whether to automatically time web client requests.", + "defaultValue": true + }, + { + "name": "management.metrics.web.client.request.autotime.percentiles", + "description": "Computed non-aggregable percentiles to publish." + }, + { + "name": "management.metrics.web.client.request.autotime.percentiles-histogram", + "description": "Whether percentile histograms should be published.", + "defaultValue": false + }, + { + "name": "management.metrics.web.server.request.autotime.enabled", + "description": "Whether to automatically time web server requests.", + "defaultValue": true + }, + { + "name": "management.metrics.web.server.request.autotime.percentiles", + "description": "Computed non-aggregable percentiles to publish." + }, + { + "name": "management.metrics.web.server.request.autotime.percentiles-histogram", + "description": "Whether percentile histograms should be published.", + "defaultValue": false + }, { "name": "management.server.ssl.ciphers", "description": "Supported SSL ciphers." diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfigurationTests.java index 40a5f02e67..ec61d507c7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfigurationTests.java @@ -17,6 +17,8 @@ package org.springframework.boot.actuate.autoconfigure.metrics.web.client; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.distribution.HistogramSnapshot; import org.junit.Rule; import org.junit.Test; @@ -100,6 +102,24 @@ public class RestTemplateMetricsConfigurationTests { }); } + @Test + public void autoTimeRequestsCanBeConfigured() { + this.contextRunner.withPropertyValues( + "management.metrics.web.client.request.autotime.enabled=true", + "management.metrics.web.client.request.autotime.percentiles=0.5,0.7", + "management.metrics.web.client.request.autotime.percentiles-histogram=true") + .run((context) -> { + MeterRegistry registry = getInitializedMeterRegistry(context); + Timer timer = registry.get("http.client.requests").timer(); + HistogramSnapshot snapshot = timer.takeSnapshot(); + assertThat(snapshot.percentileValues()).hasSize(2); + assertThat(snapshot.percentileValues()[0].percentile()) + .isEqualTo(0.5); + assertThat(snapshot.percentileValues()[1].percentile()) + .isEqualTo(0.7); + }); + } + @Test public void backsOffWhenRestTemplateBuilderIsMissing() { new ApplicationContextRunner().with(MetricsRun.simple()) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java index ce92cedb7f..8a7b1d858d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java @@ -19,6 +19,8 @@ package org.springframework.boot.actuate.autoconfigure.metrics.web.client; import java.time.Duration; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.distribution.HistogramSnapshot; import org.junit.Rule; import org.junit.Test; import reactor.core.publisher.Mono; @@ -101,6 +103,24 @@ public class WebClientMetricsConfigurationTests { }); } + @Test + public void autoTimeRequestsCanBeConfigured() { + this.contextRunner.withPropertyValues( + "management.metrics.web.client.request.autotime.enabled=true", + "management.metrics.web.client.request.autotime.percentiles=0.5,0.7", + "management.metrics.web.client.request.autotime.percentiles-histogram=true") + .run((context) -> { + MeterRegistry registry = getInitializedMeterRegistry(context); + Timer timer = registry.get("http.client.requests").timer(); + HistogramSnapshot snapshot = timer.takeSnapshot(); + assertThat(snapshot.percentileValues()).hasSize(2); + assertThat(snapshot.percentileValues()[0].percentile()) + .isEqualTo(0.5); + assertThat(snapshot.percentileValues()[1].percentile()) + .isEqualTo(0.7); + }); + } + private MeterRegistry getInitializedMeterRegistry( AssertableApplicationContext context) { WebClient webClient = mockWebClient(context.getBean(WebClient.Builder.class)); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfigurationTests.java index e61bcd04f9..d51fde7693 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfigurationTests.java @@ -102,7 +102,7 @@ public class WebFluxMetricsAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class)) .withUserConfiguration(TestController.class) .withPropertyValues( - "management.metrics.web.server.auto-time-requests=false") + "management.metrics.web.server.request.autotime.enabled=false") .run((context) -> { MeterRegistry registry = getInitializedMeterRegistry(context); assertThat(registry.find("http.server.requests").meter()).isNull(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfigurationTests.java index bb5eb901d4..1481fab1f7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfigurationTests.java @@ -26,6 +26,8 @@ import javax.servlet.http.HttpServletResponse; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.distribution.HistogramSnapshot; import org.junit.Rule; import org.junit.Test; @@ -58,6 +60,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * * @author Andy Wilkinson * @author Dmytro Nosan + * @author Tadaya Tsuyukubo */ public class WebMvcMetricsAutoConfigurationTests { @@ -137,6 +140,27 @@ public class WebMvcMetricsAutoConfigurationTests { }); } + @Test + public void autoTimeRequestsCanBeConfigured() { + this.contextRunner.withUserConfiguration(TestController.class) + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, + WebMvcAutoConfiguration.class)) + .withPropertyValues( + "management.metrics.web.server.request.autotime.enabled=true", + "management.metrics.web.server.request.autotime.percentiles=0.5,0.7", + "management.metrics.web.server.request.autotime.percentiles-histogram=true") + .run((context) -> { + MeterRegistry registry = getInitializedMeterRegistry(context); + Timer timer = registry.get("http.server.requests").timer(); + HistogramSnapshot snapshot = timer.takeSnapshot(); + assertThat(snapshot.percentileValues()).hasSize(2); + assertThat(snapshot.percentileValues()[0].percentile()) + .isEqualTo(0.5); + assertThat(snapshot.percentileValues()[1].percentile()) + .isEqualTo(0.7); + }); + } + @Test @SuppressWarnings("rawtypes") public void longTaskTimingInterceptorIsRegistered() { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Autotime.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Autotime.java new file mode 100644 index 0000000000..e02950a2ed --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Autotime.java @@ -0,0 +1,89 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.metrics; + +import java.util.List; + +/** + * Settings for requests that are automatically timed. + * + * @author Tadaya Tsuyukubo + * @author Stephane Nicoll + * @since 2.2.0 + */ +public final class Autotime { + + private boolean enabled = true; + + private boolean percentilesHistogram; + + private double[] percentiles; + + /** + * Create an instance that automatically time requests with no percentiles. + */ + public Autotime() { + } + + /** + * Create an instance with the specified settings. + * @param enabled whether requests should be automatically timed + * @param percentilesHistogram whether percentile histograms should be published + * @param percentiles computed non-aggregable percentiles to publish (can be + * {@code null}) + */ + public Autotime(boolean enabled, boolean percentilesHistogram, + List percentiles) { + this.enabled = enabled; + this.percentilesHistogram = percentilesHistogram; + this.percentiles = (percentiles != null) + ? percentiles.stream().mapToDouble(Double::doubleValue).toArray() : null; + } + + /** + * Create an instance that disable auto-timed requests. + * @return an instance that disable auto-timed requests + */ + public static Autotime disabled() { + return new Autotime(false, false, null); + } + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isPercentilesHistogram() { + return this.percentilesHistogram; + } + + public void setPercentilesHistogram(boolean percentilesHistogram) { + this.percentilesHistogram = percentilesHistogram; + } + + public double[] getPercentiles() { + return this.percentiles; + } + + public void setPercentiles(double[] percentiles) { + this.percentiles = percentiles; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java index 0d2d611f4b..d7469aca95 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.core.NamedThreadLocal; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; @@ -50,11 +51,36 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto private final String metricName; + private final Autotime autotime; + + /** + * Create a new {@code MetricsClientHttpRequestInterceptor}. + * @param meterRegistry the registry to which metrics are recorded + * @param tagProvider provider for metrics tags + * @param metricName name of the metric to record + * @deprecated since 2.2.0 in favor of + * {@link #MetricsClientHttpRequestInterceptor(MeterRegistry, RestTemplateExchangeTagsProvider, String, Autotime)} + */ MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName) { + this(meterRegistry, tagProvider, metricName, new Autotime()); + } + + /** + * Create a new {@code MetricsClientHttpRequestInterceptor}. + * @param meterRegistry the registry to which metrics are recorded + * @param tagProvider provider for metrics tags + * @param metricName name of the metric to record + * @param autotime auto timed request settings + * @since 2.2.0 + */ + MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry, + RestTemplateExchangeTagsProvider tagProvider, String metricName, + Autotime autotime) { this.tagProvider = tagProvider; this.meterRegistry = meterRegistry; this.metricName = metricName; + this.autotime = (autotime != null) ? autotime : Autotime.disabled(); } @Override @@ -67,8 +93,10 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto return response; } finally { - getTimeBuilder(request, response).register(this.meterRegistry) - .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + if (this.autotime.isEnabled()) { + getTimeBuilder(request, response).register(this.meterRegistry) + .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + } urlTemplate.remove(); } } @@ -94,6 +122,8 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto private Timer.Builder getTimeBuilder(HttpRequest request, ClientHttpResponse response) { return Timer.builder(this.metricName) + .publishPercentiles(this.autotime.getPercentiles()) + .publishPercentileHistogram(this.autotime.isPercentilesHistogram()) .tags(this.tagProvider.getTags(urlTemplate.get(), request, response)) .description("Timer of RestTemplate operation"); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizer.java index 0755feadaa..9efce605a5 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.util.List; import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; @@ -45,11 +46,30 @@ public class MetricsRestTemplateCustomizer implements RestTemplateCustomizer { * @param meterRegistry the meter registry * @param tagProvider the tag provider * @param metricName the name of the recorded metric + * @deprecated since 2.2.0 in favor of + * {@link #MetricsRestTemplateCustomizer(MeterRegistry, RestTemplateExchangeTagsProvider, String, Autotime)} */ public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName) { + this(meterRegistry, tagProvider, metricName, new Autotime()); + } + + /** + * Creates a new {@code MetricsRestTemplateInterceptor}. When {@code autoTimeRequests} + * is set to {@code true}, the interceptor records metrics using the given + * {@code meterRegistry} with tags provided by the given {@code tagProvider} and with + * {@link Autotime auto-timed request configuration}. + * @param meterRegistry the meter registry + * @param tagProvider the tag provider + * @param metricName the name of the recorded metric + * @param autotime auto-timed request settings + * @since 2.2.0 + */ + public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry, + RestTemplateExchangeTagsProvider tagProvider, String metricName, + Autotime autotime) { this.interceptor = new MetricsClientHttpRequestInterceptor(meterRegistry, - tagProvider, metricName); + tagProvider, metricName, autotime); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizer.java index 78d60852c8..1f1984e472 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizer.java @@ -18,6 +18,7 @@ package org.springframework.boot.actuate.metrics.web.reactive.client; import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; import org.springframework.web.reactive.function.client.WebClient; @@ -39,11 +40,30 @@ public class MetricsWebClientCustomizer implements WebClientCustomizer { * @param meterRegistry the meter registry * @param tagProvider the tag provider * @param metricName the name of the recorded metric + * @deprecated since 2.2.0 in favor of + * {@link #MetricsWebClientCustomizer(MeterRegistry, WebClientExchangeTagsProvider, String, Autotime)} */ + @Deprecated public MetricsWebClientCustomizer(MeterRegistry meterRegistry, WebClientExchangeTagsProvider tagProvider, String metricName) { + this(meterRegistry, tagProvider, metricName, new Autotime()); + } + + /** + * Create a new {@code MetricsWebClientFilterFunction} that will record metrics using + * the given {@code meterRegistry} with tags provided by the given + * {@code tagProvider}. + * @param meterRegistry the meter registry + * @param tagProvider the tag provider + * @param metricName the name of the recorded metric + * @param autotime auto-timed request settings + * @since 2.2.0 + */ + public MetricsWebClientCustomizer(MeterRegistry meterRegistry, + WebClientExchangeTagsProvider tagProvider, String metricName, + Autotime autotime) { this.filterFunction = new MetricsWebClientFilterFunction(meterRegistry, - tagProvider, metricName); + tagProvider, metricName, autotime); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunction.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunction.java index 63fd9c00c7..0943fe7f33 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunction.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; import reactor.core.publisher.Mono; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; @@ -33,6 +34,7 @@ import org.springframework.web.reactive.function.client.ExchangeFunction; * record metrics. * * @author Brian Clozel + * @author Tadaya Tsuyukubo * @since 2.1.0 */ public class MetricsWebClientFilterFunction implements ExchangeFilterFunction { @@ -46,25 +48,54 @@ public class MetricsWebClientFilterFunction implements ExchangeFilterFunction { private final String metricName; + private final Autotime autotime; + + /** + * Create a new {@code MetricsWebClientFilterFunction}. + * @param meterRegistry the registry to which metrics are recorded + * @param tagProvider provider for metrics tags + * @param metricName name of the metric to record + * @deprecated since 2.2.0 in favor of + * {@link #MetricsWebClientFilterFunction(MeterRegistry, WebClientExchangeTagsProvider, String, Autotime)} + */ + @Deprecated public MetricsWebClientFilterFunction(MeterRegistry meterRegistry, WebClientExchangeTagsProvider tagProvider, String metricName) { + this(meterRegistry, tagProvider, metricName, new Autotime()); + } + + /** + * Create a new {@code MetricsWebClientFilterFunction}. + * @param meterRegistry the registry to which metrics are recorded + * @param tagProvider provider for metrics tags + * @param metricName name of the metric to record + * @param autotime auto-timed request settings + * @since 2.2.0 + */ + public MetricsWebClientFilterFunction(MeterRegistry meterRegistry, + WebClientExchangeTagsProvider tagProvider, String metricName, + Autotime autotime) { this.meterRegistry = meterRegistry; this.tagProvider = tagProvider; this.metricName = metricName; + this.autotime = (autotime != null) ? autotime : Autotime.disabled(); } @Override public Mono filter(ClientRequest clientRequest, ExchangeFunction exchangeFunction) { return exchangeFunction.exchange(clientRequest).doOnEach((signal) -> { - if (!signal.isOnComplete()) { + if (!signal.isOnComplete() && this.autotime.isEnabled()) { Long startTime = signal.getContext().get(METRICS_WEBCLIENT_START_TIME); ClientResponse clientResponse = signal.get(); Throwable throwable = signal.getThrowable(); Iterable tags = this.tagProvider.tags(clientRequest, clientResponse, throwable); - Timer.builder(this.metricName).tags(tags) - .description("Timer of WebClient operation") + Timer.builder(this.metricName) + .publishPercentiles(this.autotime.getPercentiles()) + .publishPercentileHistogram( + this.autotime.isPercentilesHistogram()) + .tags(tags).description("Timer of WebClient operation") .register(this.meterRegistry) .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java index 64a698a801..16861b014d 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java @@ -20,9 +20,11 @@ import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -46,19 +48,43 @@ public class MetricsWebFilter implements WebFilter { private final String metricName; - private final boolean autoTimeRequests; + private final Autotime autotime; + /** + * Create a new {@code MetricsWebFilter}. + * @param registry the registry to which metrics are recorded + * @param tagsProvider provider for metrics tags + * @param metricName name of the metric to record + * @param autoTimeRequests if requests should be automatically timed + * @deprecated since 2.2.0 in favor of + * {@link #MetricsWebFilter(MeterRegistry, WebFluxTagsProvider, String, Autotime)} + */ + @Deprecated public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider, String metricName, boolean autoTimeRequests) { + this(registry, tagsProvider, metricName, + new Autotime(autoTimeRequests, false, null)); + } + + /** + * Create a new {@code MetricsWebFilter}. + * @param registry the registry to which metrics are recorded + * @param tagsProvider provider for metrics tags + * @param metricName name of the metric to record + * @param autotime auto timed request settings + * @since 2.2.0 + */ + public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider, + String metricName, Autotime autotime) { this.registry = registry; this.tagsProvider = tagsProvider; this.metricName = metricName; - this.autoTimeRequests = autoTimeRequests; + this.autotime = (autotime != null) ? autotime : Autotime.disabled(); } @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - if (this.autoTimeRequests) { + if (this.autotime.isEnabled()) { return chain.filter(exchange).compose((call) -> filter(exchange, call)); } return chain.filter(exchange); @@ -67,29 +93,27 @@ public class MetricsWebFilter implements WebFilter { private Publisher filter(ServerWebExchange exchange, Mono call) { long start = System.nanoTime(); ServerHttpResponse response = exchange.getResponse(); - return call.doOnSuccess((done) -> success(exchange, start)).doOnError((cause) -> { - if (response.isCommitted()) { - error(exchange, start, cause); - } - else { - response.beforeCommit(() -> { - error(exchange, start, cause); - return Mono.empty(); + return call.doOnSuccess((done) -> record(exchange, start, null)) + .doOnError((cause) -> { + if (response.isCommitted()) { + record(exchange, start, cause); + } + else { + response.beforeCommit(() -> { + record(exchange, start, cause); + return Mono.empty(); + }); + } }); - } - }); } - private void success(ServerWebExchange exchange, long start) { - Iterable tags = this.tagsProvider.httpRequestTags(exchange, null); - this.registry.timer(this.metricName, tags).record(System.nanoTime() - start, - TimeUnit.NANOSECONDS); - } - - private void error(ServerWebExchange exchange, long start, Throwable cause) { + private void record(ServerWebExchange exchange, long start, Throwable cause) { Iterable tags = this.tagsProvider.httpRequestTags(exchange, cause); - this.registry.timer(this.metricName, tags).record(System.nanoTime() - start, - TimeUnit.NANOSECONDS); + Timer.builder(this.metricName).tags(tags) + .publishPercentiles(this.autotime.getPercentiles()) + .publishPercentileHistogram(this.autotime.isPercentilesHistogram()) + .register(this.registry) + .record(System.nanoTime() - start, TimeUnit.NANOSECONDS); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java index 088fbae655..c210c77974 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java @@ -34,6 +34,7 @@ import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.Timer.Builder; import io.micrometer.core.instrument.Timer.Sample; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.core.annotation.MergedAnnotationCollectors; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.http.HttpStatus; @@ -59,7 +60,7 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { private final String metricName; - private final boolean autoTimeRequests; + private final Autotime autotime; /** * Create a new {@link WebMvcMetricsFilter} instance. @@ -68,13 +69,30 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { * @param metricName the metric name * @param autoTimeRequests if requests should be automatically timed * @since 2.0.7 + * @deprecated since 2.2.0 in favor of + * {@link #WebMvcMetricsFilter(MeterRegistry, WebMvcTagsProvider, String, Autotime)} */ + @Deprecated public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider, String metricName, boolean autoTimeRequests) { + this(registry, tagsProvider, metricName, + new Autotime(autoTimeRequests, false, null)); + } + + /** + * Create a new {@link WebMvcMetricsFilter} instance. + * @param registry the meter registry + * @param tagsProvider the tags provider + * @param metricName the metric name + * @param autotime auto timed request settings + * @since 2.2.0 + */ + public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider, + String metricName, Autotime autotime) { this.registry = registry; this.tagsProvider = tagsProvider; this.metricName = metricName; - this.autoTimeRequests = autoTimeRequests; + this.autotime = autotime; } @Override @@ -155,8 +173,12 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { Supplier> tags = () -> this.tagsProvider.getTags(request, response, handlerObject, exception); if (annotations.isEmpty()) { - if (this.autoTimeRequests) { - stop(timerSample, tags, Timer.builder(this.metricName)); + if (this.autotime.isEnabled()) { + stop(timerSample, tags, + Timer.builder(this.metricName) + .publishPercentiles(this.autotime.getPercentiles()) + .publishPercentileHistogram( + this.autotime.isPercentilesHistogram())); } } else { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizerTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizerTests.java index 00b9a5361c..89369bf8eb 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizerTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizerTests.java @@ -27,6 +27,7 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.test.web.client.MockRestServiceServer; @@ -58,7 +59,8 @@ public class MetricsRestTemplateCustomizerTests { this.restTemplate = new RestTemplate(); this.mockServer = MockRestServiceServer.createServer(this.restTemplate); this.customizer = new MetricsRestTemplateCustomizer(this.registry, - new DefaultRestTemplateExchangeTagsProvider(), "http.client.requests"); + new DefaultRestTemplateExchangeTagsProvider(), "http.client.requests", + new Autotime()); this.customizer.customize(this.restTemplate); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizerTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizerTests.java index 63ba7bb154..8f82938b22 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizerTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientCustomizerTests.java @@ -40,7 +40,7 @@ public class MetricsWebClientCustomizerTests { @Before public void setup() { this.customizer = new MetricsWebClientCustomizer(mock(MeterRegistry.class), - mock(WebClientExchangeTagsProvider.class), "test"); + mock(WebClientExchangeTagsProvider.class), "test", null); this.clientBuilder = WebClient.builder(); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunctionTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunctionTests.java index 41475b12b0..9c24fd2c3a 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunctionTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/MetricsWebClientFilterFunctionTests.java @@ -30,6 +30,7 @@ import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Mono; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.web.reactive.function.client.ClientRequest; @@ -63,7 +64,8 @@ public class MetricsWebClientFilterFunctionTests { public void setup() { this.registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock()); this.filterFunction = new MetricsWebClientFilterFunction(this.registry, - new DefaultWebClientExchangeTagsProvider(), "http.client.requests"); + new DefaultWebClientExchangeTagsProvider(), "http.client.requests", + new Autotime()); this.response = mock(ClientResponse.class); this.exchange = (r) -> Mono.just(this.response); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilterTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilterTests.java index 83bca94ed8..dcae4bafcd 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilterTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Mono; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.reactive.HandlerMapping; @@ -50,7 +51,7 @@ public class MetricsWebFilterTests { MockClock clock = new MockClock(); this.registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, clock); this.webFilter = new MetricsWebFilter(this.registry, - new DefaultWebFluxTagsProvider(), REQUEST_METRICS_NAME, true); + new DefaultWebFluxTagsProvider(), REQUEST_METRICS_NAME, new Autotime()); } @Test diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterAutoTimedTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterAutoTimedTests.java index b73f4b5d6a..03b5290f86 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterAutoTimedTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterAutoTimedTests.java @@ -16,9 +16,13 @@ package org.springframework.boot.actuate.metrics.web.servlet; +import java.util.Arrays; + import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MockClock; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.distribution.HistogramSnapshot; import io.micrometer.core.instrument.simple.SimpleConfig; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.Before; @@ -26,6 +30,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -48,6 +53,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * Test for {@link WebMvcMetricsFilter} with auto-timed enabled. * * @author Jon Schneider + * @author Tadaya Tsuyukubo */ @RunWith(SpringRunner.class) @WebAppConfiguration @@ -73,8 +79,13 @@ public class WebMvcMetricsFilterAutoTimedTests { @Test public void metricsCanBeAutoTimed() throws Exception { this.mvc.perform(get("/api/10")).andExpect(status().isOk()); - assertThat(this.registry.get("http.server.requests").tags("status", "200").timer() - .count()).isEqualTo(1L); + Timer timer = this.registry.get("http.server.requests").tags("status", "200") + .timer(); + assertThat(timer.count()).isEqualTo(1L); + HistogramSnapshot snapshot = timer.takeSnapshot(); + assertThat(snapshot.percentileValues()).hasSize(2); + assertThat(snapshot.percentileValues()[0].percentile()).isEqualTo(0.5); + assertThat(snapshot.percentileValues()[1].percentile()).isEqualTo(0.95); } @Configuration(proxyBeanMethods = false) @@ -96,7 +107,8 @@ public class WebMvcMetricsFilterAutoTimedTests { public WebMvcMetricsFilter webMetricsFilter(WebApplicationContext context, MeterRegistry registry) { return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(), - "http.server.requests", true); + "http.server.requests", + new Autotime(true, true, Arrays.asList(0.5, 0.95))); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterTests.java index 07b375fcd8..f7ef2e1346 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilterTests.java @@ -56,6 +56,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -372,7 +373,7 @@ public class WebMvcMetricsFilterTests { WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry, WebApplicationContext ctx) { return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(), - "http.server.requests", true); + "http.server.requests", new Autotime()); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java index 957b6dbd9d..06ae5ccb3b 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.Autotime; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; @@ -111,7 +112,7 @@ public class WebMvcMetricsIntegrationTests { public WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry, WebApplicationContext ctx) { return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(), - "http.server.requests", true); + "http.server.requests", new Autotime()); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 0811ac17a3..3b310afbf8 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -1801,9 +1801,9 @@ application's absolute start time [[production-ready-metrics-spring-mvc]] ==== Spring MVC Metrics Auto-configuration enables the instrumentation of requests handled by Spring MVC. When -`management.metrics.web.server.auto-time-requests` is `true`, this instrumentation occurs -for all requests. Alternatively, when set to `false`, you can enable instrumentation by -adding `@Timed` to a request-handling method: +`management.metrics.web.server.request.autotime.enabled` is `true`, this instrumentation +occurs for all requests. Alternatively, when set to `false`, you can enable +instrumentation by adding `@Timed` to a request-handling method: [source,java,indent=0] ---- @@ -1896,8 +1896,8 @@ To customize the tags, provide a `@Bean` that implements `WebFluxTagsProvider`. [[production-ready-metrics-jersey-server]] ==== Jersey Server Metrics Auto-configuration enables the instrumentation of requests handled by the Jersey JAX-RS -implementation. When `management.metrics.web.server.auto-time-requests` is `true`, this -instrumentation occurs for all requests. Alternatively, when set to `false`, you can +implementation. When `management.metrics.web.server.request.autotime.enabled` is `true`, +this instrumentation occurs for all requests. Alternatively, when set to `false`, you can enable instrumentation by adding `@Timed` to a request-handling method: [source,java,indent=0]