From 4df6f5dee0be7faf0f15fb496113f5b19c9665b2 Mon Sep 17 00:00:00 2001 From: Tadaya Tsuyukubo Date: Wed, 17 Apr 2019 17:46:06 -0700 Subject: [PATCH 1/2] Allow configuration of auto-timed metrics When `management.metrics.web.server.auto-time-requests` 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 `@Timed` configuration and there is no way to customize the configuration of those auto-timed controller metrics. This commit adds default configurations to auto-timed requests on both client and server sides. See gh-15988 --- .../metrics/MetricsProperties.java | 166 +++++++++++++++--- .../JerseyServerMetricsAutoConfiguration.java | 8 +- .../HttpClientMetricsAutoConfiguration.java | 2 +- .../RestTemplateMetricsConfiguration.java | 10 +- .../client/WebClientMetricsConfiguration.java | 2 +- .../WebFluxMetricsAutoConfiguration.java | 11 +- .../WebMvcMetricsAutoConfiguration.java | 9 +- .../WebFluxMetricsAutoConfigurationTests.java | 2 +- .../WebMvcMetricsAutoConfigurationTests.java | 24 +++ .../MetricsClientHttpRequestInterceptor.java | 48 ++++- .../client/MetricsRestTemplateCustomizer.java | 22 +++ .../MetricsWebClientFilterFunction.java | 45 ++++- .../web/reactive/server/MetricsWebFilter.java | 73 ++++++-- .../web/servlet/WebMvcMetricsFilter.java | 36 +++- .../MetricsRestTemplateCustomizerTests.java | 3 +- .../server/MetricsWebFilterTests.java | 3 +- .../WebMvcMetricsFilterAutoTimedTests.java | 16 +- .../web/servlet/WebMvcMetricsFilterTests.java | 2 +- .../WebMvcMetricsIntegrationTests.java | 2 +- .../asciidoc/production-ready-features.adoc | 4 +- 20 files changed, 416 insertions(+), 72 deletions(-) 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 2bca5a6036d..bf0c9f0e6f7 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 @@ -16,10 +16,13 @@ package org.springframework.boot.actuate.autoconfigure.metrics; +import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; /** * {@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,79 @@ public class MetricsProperties { public static class Client { + private final ClientRequest request = new ClientRequest(); + + /** + * Maximum number of unique URI tag values allowed. After the max number of + * tag values is reached, metrics with additional tag values are denied by + * filter. + */ + private int maxUriTags = 100; + /** - * Name of the metric for sent requests. + * Get name of the metric for received requests. + * @return request metric name + * @deprecated since 2.2.0 in favor of {@link ClientRequest#getMetricName()} */ - private String requestsMetricName = "http.client.requests"; + @DeprecatedConfigurationProperty( + replacement = "management.metrics.web.client.request.metric-name") + public String getRequestsMetricName() { + return this.request.getMetricName(); + } + + /** + * Set name of the metric for received requests. + * @param requestsMetricName request metric name + * @deprecated since 2.2.0 in favor of + * {@link ClientRequest#setMetricName(String)} + */ + public void setRequestsMetricName(String requestsMetricName) { + this.request.setMetricName(requestsMetricName); + } + + public ClientRequest getRequest() { + return this.request; + } + + public int getMaxUriTags() { + return this.maxUriTags; + } + + public void setMaxUriTags(int maxUriTags) { + this.maxUriTags = maxUriTags; + } + + public static class ClientRequest { + + /** + * Name of the metric for sent requests. + */ + private String metricName = "http.client.requests"; + + /** + * Automatically time requests. + */ + 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 { + + private final ServerRequest request = new ServerRequest(); /** * Maximum number of unique URI tag values allowed. After the max number of @@ -106,12 +179,29 @@ public class MetricsProperties { */ private int maxUriTags = 100; + /** + * Get name of the metric for received 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 name of the metric for received 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 ServerRequest getRequest() { + return this.request; } public int getMaxUriTags() { @@ -122,9 +212,35 @@ public class MetricsProperties { this.maxUriTags = maxUriTags; } + public static class ServerRequest { + + /** + * Name of the metric for received requests. + */ + private String metricName = "http.server.requests"; + + /** + * Automatically time requests. + */ + 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 { + public static class AutoTime { /** * Whether requests handled by Spring MVC, WebFlux or Jersey should be @@ -132,42 +248,44 @@ public class MetricsProperties { * on account of request mapping timings, disable this and use 'Timed' on a * per request mapping basis as needed. */ - private boolean autoTimeRequests = true; + private boolean enabled = true; /** - * Name of the metric for received requests. + * Default percentiles when @Timed annotation is not presented on the + * corresponding request handler. Any @Timed annotation presented will have + * precedence. */ - private String requestsMetricName = "http.server.requests"; + private List defaultPercentiles = new ArrayList<>(); /** - * Maximum number of unique URI tag values allowed. After the max number of - * tag values is reached, metrics with additional tag values are denied by - * filter. + * Default histogram when @Timed annotation is not presented on the + * corresponding request handler. Any @Timed annotation presented will have + * precedence. */ - private int maxUriTags = 100; + private boolean defaultHistogram; - public boolean isAutoTimeRequests() { - return this.autoTimeRequests; + public boolean isEnabled() { + return this.enabled; } - public void setAutoTimeRequests(boolean autoTimeRequests) { - this.autoTimeRequests = autoTimeRequests; + public void setEnabled(boolean enabled) { + this.enabled = enabled; } - public String getRequestsMetricName() { - return this.requestsMetricName; + public List getDefaultPercentiles() { + return this.defaultPercentiles; } - public void setRequestsMetricName(String requestsMetricName) { - this.requestsMetricName = requestsMetricName; + public void setDefaultPercentiles(List defaultPercentiles) { + this.defaultPercentiles = defaultPercentiles; } - public int getMaxUriTags() { - return this.maxUriTags; + public boolean isDefaultHistogram() { + return this.defaultHistogram; } - public void setMaxUriTags(int maxUriTags) { - this.maxUriTags = maxUriTags; + public void setDefaultHistogram(boolean defaultHistogram) { + this.defaultHistogram = defaultHistogram; } } 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 ec10ae311f2..d5d6d923ec4 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 def5e7174a1..6fdb0a8ad0b 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 6ec64b51a39..7326b90743d 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,8 @@ 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.AutoTime; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Client; 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 +55,13 @@ class RestTemplateMetricsConfiguration { MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider restTemplateExchangeTagsProvider, MetricsProperties properties) { + + Client client = properties.getWeb().getClient(); + AutoTime autoTime = client.getRequest().getAutoTime(); return new MetricsRestTemplateCustomizer(meterRegistry, - restTemplateExchangeTagsProvider, - properties.getWeb().getClient().getRequestsMetricName()); + restTemplateExchangeTagsProvider, client.getRequest().getMetricName(), + autoTime.isEnabled(), autoTime.getDefaultPercentiles(), + autoTime.isDefaultHistogram()); } } 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 dbbf4e06283..5a88d721665 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 @@ -49,7 +49,7 @@ class WebClientMetricsConfiguration { MeterRegistry meterRegistry, WebClientExchangeTagsProvider tagsProvider, MetricsProperties properties) { return new MetricsWebClientCustomizer(meterRegistry, tagsProvider, - properties.getWeb().getClient().getRequestsMetricName()); + properties.getWeb().getClient().getRequest().getMetricName()); } } 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 87280c54484..1d03e871227 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,8 @@ 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.AutoTime; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server; 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 +67,18 @@ public class WebFluxMetricsAutoConfiguration { @Bean public MetricsWebFilter webfluxMetrics(MeterRegistry registry, WebFluxTagsProvider tagConfigurer) { + Server serverProperties = this.properties.getWeb().getServer(); + AutoTime autotime = serverProperties.getRequest().getAutoTime(); return new MetricsWebFilter(registry, tagConfigurer, - this.properties.getWeb().getServer().getRequestsMetricName(), - this.properties.getWeb().getServer().isAutoTimeRequests()); + serverProperties.getRequest().getMetricName(), autotime.isEnabled(), + autotime.getDefaultPercentiles(), autotime.isDefaultHistogram()); } @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 ffa95accfe3..4463fac6155 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,6 +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.AutoTime; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; @@ -79,9 +80,10 @@ public class WebMvcMetricsAutoConfiguration { public FilterRegistrationBean webMvcMetricsFilter( MeterRegistry registry, WebMvcTagsProvider tagsProvider) { Server serverProperties = this.properties.getWeb().getServer(); + AutoTime autotime = serverProperties.getRequest().getAutoTime(); WebMvcMetricsFilter filter = new WebMvcMetricsFilter(registry, tagsProvider, - serverProperties.getRequestsMetricName(), - serverProperties.isAutoTimeRequests()); + serverProperties.getRequest().getMetricName(), autotime.isEnabled(), + autotime.getDefaultPercentiles(), autotime.isDefaultHistogram()); FilterRegistrationBean registration = new FilterRegistrationBean<>( filter); registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); @@ -92,7 +94,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/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 e61bcd04f99..6b15f1a95b7 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.auto-time.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 bb5eb901d44..898c499da73 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 autoTimeRequestsDefaultValues() { + this.contextRunner.withUserConfiguration(TestController.class) + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, + WebMvcAutoConfiguration.class)) + .withPropertyValues( + "management.metrics.web.server.request.auto-time.enabled=true", + "management.metrics.web.server.request.auto-time.default-percentiles=0.5,0.7", + "management.metrics.web.server.request.auto-time.default-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/web/client/MetricsClientHttpRequestInterceptor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java index 0d2d611f4b0..63c2a4d5535 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 @@ -18,6 +18,7 @@ package org.springframework.boot.actuate.metrics.web.client; import java.io.IOException; import java.net.URI; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -50,11 +51,49 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto private final String metricName; + private final boolean autoTimeRequests; + + private final double[] percentiles; + + private final boolean histogram; + + /** + * 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, boolean, List, boolean)} + */ MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName) { + this(meterRegistry, tagProvider, metricName, true, null, false); + } + + /** + * 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 autoTimeRequests if requests should be automatically timed + * @param percentileList percentiles for auto time requests + * @param histogram histogram or not for auto time requests + * @since 2.2.0 + */ + MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry, + RestTemplateExchangeTagsProvider tagProvider, String metricName, + boolean autoTimeRequests, List percentileList, boolean histogram) { + + double[] percentiles = (percentileList != null) + ? percentileList.stream().mapToDouble(Double::doubleValue).toArray() + : null; + this.tagProvider = tagProvider; this.meterRegistry = meterRegistry; this.metricName = metricName; + this.autoTimeRequests = autoTimeRequests; + this.percentiles = percentiles; + this.histogram = histogram; } @Override @@ -67,8 +106,10 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto return response; } finally { - getTimeBuilder(request, response).register(this.meterRegistry) - .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + if (this.autoTimeRequests) { + getTimeBuilder(request, response).register(this.meterRegistry) + .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + } urlTemplate.remove(); } } @@ -93,7 +134,8 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto private Timer.Builder getTimeBuilder(HttpRequest request, ClientHttpResponse response) { - return Timer.builder(this.metricName) + return Timer.builder(this.metricName).publishPercentiles(this.percentiles) + .publishPercentileHistogram(this.histogram) .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 0755feadaa0..ac3892db2f6 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 @@ -45,6 +45,8 @@ 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, boolean, List, boolean)} */ public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName) { @@ -52,6 +54,26 @@ public class MetricsRestTemplateCustomizer implements RestTemplateCustomizer { tagProvider, metricName); } + /** + * 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 + * {@code percentileList} and {@code histogram} configurations. + * @param meterRegistry the meter registry + * @param tagProvider the tag provider + * @param metricName the name of the recorded metric + * @param autoTimeRequests if requests should be automatically timed + * @param percentileList percentiles for auto time requests + * @param histogram histogram or not for auto time requests + * @since 2.2.0 + */ + public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry, + RestTemplateExchangeTagsProvider tagProvider, String metricName, + boolean autoTimeRequests, List percentileList, boolean histogram) { + this.interceptor = new MetricsClientHttpRequestInterceptor(meterRegistry, + tagProvider, metricName, autoTimeRequests, percentileList, histogram); + } + @Override public void customize(RestTemplate restTemplate) { UriTemplateHandler templateHandler = restTemplate.getUriTemplateHandler(); 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 63fd9c00c73..0fc99967f82 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 @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.metrics.web.reactive.client; +import java.util.List; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; @@ -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,24 +48,63 @@ public class MetricsWebClientFilterFunction implements ExchangeFilterFunction { private final String metricName; + private final boolean autoTimeRequests; + + private final double[] percentiles; + + private final boolean histogram; + + /** + * 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, boolean, List, boolean)} + */ public MetricsWebClientFilterFunction(MeterRegistry meterRegistry, WebClientExchangeTagsProvider tagProvider, String metricName) { + this(meterRegistry, tagProvider, metricName, true, null, false); + } + + /** + * 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 autoTimeRequests if requests should be automatically timed + * @param percentileList percentiles for auto time requests + * @param histogram histogram or not for auto time requests + * @since 2.2.0 + */ + public MetricsWebClientFilterFunction(MeterRegistry meterRegistry, + WebClientExchangeTagsProvider tagProvider, String metricName, + boolean autoTimeRequests, List percentileList, boolean histogram) { + + double[] percentiles = (percentileList != null) + ? percentileList.stream().mapToDouble(Double::doubleValue).toArray() + : null; + this.meterRegistry = meterRegistry; this.tagProvider = tagProvider; this.metricName = metricName; + this.autoTimeRequests = autoTimeRequests; + this.percentiles = percentiles; + this.histogram = histogram; } @Override public Mono filter(ClientRequest clientRequest, ExchangeFunction exchangeFunction) { return exchangeFunction.exchange(clientRequest).doOnEach((signal) -> { - if (!signal.isOnComplete()) { + if (!signal.isOnComplete() && this.autoTimeRequests) { 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) + Timer.builder(this.metricName).publishPercentiles(this.percentiles) + .publishPercentileHistogram(this.histogram).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 64a698a801c..6659a93025f 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 @@ -16,10 +16,12 @@ package org.springframework.boot.actuate.metrics.web.reactive.server; +import java.util.List; 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; @@ -48,12 +50,49 @@ public class MetricsWebFilter implements WebFilter { private final boolean autoTimeRequests; + private final double[] percentiles; + + private final boolean histogram; + + /** + * 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, boolean, List, boolean)} + */ + @Deprecated public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider, String metricName, boolean autoTimeRequests) { + this(registry, tagsProvider, metricName, autoTimeRequests, null, false); + } + + /** + * 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 + * @param percentileList percentiles for auto time requests + * @param histogram histogram or not for auto time requests + * @since 2.2.0 + */ + public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider, + String metricName, boolean autoTimeRequests, List percentileList, + boolean histogram) { + + double[] percentiles = (percentileList != null) + ? percentileList.stream().mapToDouble(Double::doubleValue).toArray() + : null; + this.registry = registry; this.tagsProvider = tagsProvider; this.metricName = metricName; this.autoTimeRequests = autoTimeRequests; + this.percentiles = percentiles; + this.histogram = histogram; } @Override @@ -67,29 +106,25 @@ 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.percentiles) + .publishPercentileHistogram(this.histogram).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 088fbae655a..05913ef0f4d 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 @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.metrics.web.servlet; import java.io.IOException; import java.lang.reflect.AnnotatedElement; import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.function.Supplier; @@ -61,6 +62,10 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { private final boolean autoTimeRequests; + private final double[] autoTimeRequestsPercentiles; + + private final boolean autoTimeRequestsHistogram; + /** * Create a new {@link WebMvcMetricsFilter} instance. * @param registry the meter registry @@ -68,13 +73,40 @@ 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.1.4 in favor of + * {@link #WebMvcMetricsFilter(MeterRegistry, WebMvcTagsProvider, String, boolean, List, boolean)} */ + @Deprecated public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider, String metricName, boolean autoTimeRequests) { + this(registry, tagsProvider, metricName, autoTimeRequests, null, false); + } + + /** + * Create a new {@link WebMvcMetricsFilter} instance. + * @param registry the meter registry + * @param tagsProvider the tags provider + * @param metricName the metric name + * @param autoTimeRequests if requests should be automatically timed + * @param autoTimeRequestsPercentiles default percentiles if requests are auto timed + * @param autoTimeRequestsHistogram default histogram flag if requests are auto timed + * @since 2.2.0 + */ + public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider, + String metricName, boolean autoTimeRequests, + List autoTimeRequestsPercentiles, boolean autoTimeRequestsHistogram) { + + double[] percentiles = (autoTimeRequestsPercentiles != null) + ? autoTimeRequestsPercentiles.stream().mapToDouble(Double::doubleValue) + .toArray() + : null; + this.registry = registry; this.tagsProvider = tagsProvider; this.metricName = metricName; this.autoTimeRequests = autoTimeRequests; + this.autoTimeRequestsPercentiles = percentiles; + this.autoTimeRequestsHistogram = autoTimeRequestsHistogram; } @Override @@ -156,7 +188,9 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { handlerObject, exception); if (annotations.isEmpty()) { if (this.autoTimeRequests) { - stop(timerSample, tags, Timer.builder(this.metricName)); + stop(timerSample, tags, Timer.builder(this.metricName) + .publishPercentiles(this.autoTimeRequestsPercentiles) + .publishPercentileHistogram(this.autoTimeRequestsHistogram)); } } 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 00b9a5361cd..1c7c01fc933 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 @@ -58,7 +58,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", + true, null, false); this.customizer.customize(this.restTemplate); } 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 83bca94ed80..fc09bce730f 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 @@ -50,7 +50,8 @@ 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, true, null, + false); } @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 b73f4b5d6ab..df84c90372f 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; @@ -48,6 +52,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 +78,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 +106,7 @@ public class WebMvcMetricsFilterAutoTimedTests { public WebMvcMetricsFilter webMetricsFilter(WebApplicationContext context, MeterRegistry registry) { return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(), - "http.server.requests", true); + "http.server.requests", true, Arrays.asList(0.5, 0.95), true); } } 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 07b375fcd85..641af163101 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 @@ -372,7 +372,7 @@ public class WebMvcMetricsFilterTests { WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry, WebApplicationContext ctx) { return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(), - "http.server.requests", true); + "http.server.requests", true, null, false); } } 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 957b6dbd9da..a30abc38eff 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 @@ -111,7 +111,7 @@ public class WebMvcMetricsIntegrationTests { public WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry, WebApplicationContext ctx) { return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(), - "http.server.requests", true); + "http.server.requests", true, null, false); } @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 0811ac17a3f..89734a0f200 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,7 +1801,7 @@ 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 +`management.metrics.web.server.request.auto-time.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: @@ -1896,7 +1896,7 @@ 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 +implementation. When `management.metrics.web.server.request.auto-time.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: From 8045bf1f50ba7e41552d5e3b585c9ebca39eecd3 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 1 May 2019 08:16:54 +0200 Subject: [PATCH 2/2] Polish "Allow configuration of auto-timed metrics" Closes gh-15988 --- .../metrics/MetricsProperties.java | 94 +++++-------------- .../JerseyServerMetricsAutoConfiguration.java | 2 +- .../RestTemplateMetricsConfiguration.java | 12 +-- .../client/WebClientMetricsConfiguration.java | 4 +- .../WebFluxMetricsAutoConfiguration.java | 11 +-- .../WebMvcMetricsAutoConfiguration.java | 9 +- ...itional-spring-configuration-metadata.json | 28 ++++++ ...RestTemplateMetricsConfigurationTests.java | 20 ++++ .../WebClientMetricsConfigurationTests.java | 20 ++++ .../WebFluxMetricsAutoConfigurationTests.java | 2 +- .../WebMvcMetricsAutoConfigurationTests.java | 8 +- .../boot/actuate/metrics/Autotime.java | 89 ++++++++++++++++++ .../MetricsClientHttpRequestInterceptor.java | 36 +++---- .../client/MetricsRestTemplateCustomizer.java | 18 ++-- .../client/MetricsWebClientCustomizer.java | 22 ++++- .../MetricsWebClientFilterFunction.java | 40 +++----- .../web/reactive/server/MetricsWebFilter.java | 37 +++----- .../web/servlet/WebMvcMetricsFilter.java | 42 +++------ .../MetricsRestTemplateCustomizerTests.java | 3 +- .../MetricsWebClientCustomizerTests.java | 2 +- .../MetricsWebClientFilterFunctionTests.java | 4 +- .../server/MetricsWebFilterTests.java | 6 +- .../WebMvcMetricsFilterAutoTimedTests.java | 4 +- .../web/servlet/WebMvcMetricsFilterTests.java | 3 +- .../WebMvcMetricsIntegrationTests.java | 3 +- .../asciidoc/production-ready-features.adoc | 10 +- 26 files changed, 305 insertions(+), 224 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Autotime.java 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 bf0c9f0e6f7..ec9a83758a5 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 @@ -16,13 +16,13 @@ package org.springframework.boot.actuate.autoconfigure.metrics; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; 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 @@ -107,8 +107,12 @@ public class MetricsProperties { */ private int maxUriTags = 100; + public ClientRequest getRequest() { + return this.request; + } + /** - * Get name of the metric for received requests. + * Return the name of the metric for client requests. * @return request metric name * @deprecated since 2.2.0 in favor of {@link ClientRequest#getMetricName()} */ @@ -119,7 +123,7 @@ public class MetricsProperties { } /** - * Set name of the metric for received requests. + * 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)} @@ -128,10 +132,6 @@ public class MetricsProperties { this.request.setMetricName(requestsMetricName); } - public ClientRequest getRequest() { - return this.request; - } - public int getMaxUriTags() { return this.maxUriTags; } @@ -148,11 +148,12 @@ public class MetricsProperties { private String metricName = "http.client.requests"; /** - * Automatically time requests. + * Auto-timed request settings. */ - private final AutoTime autoTime = new AutoTime(); + @NestedConfigurationProperty + private final Autotime autoTime = new Autotime(); - public AutoTime getAutoTime() { + public Autotime getAutotime() { return this.autoTime; } @@ -179,8 +180,12 @@ public class MetricsProperties { */ private int maxUriTags = 100; + public ServerRequest getRequest() { + return this.request; + } + /** - * Get name of the metric for received requests. + * Return name of the metric for server requests. * @return request metric name * @deprecated since 2.2.0 in favor of {@link ServerRequest#getMetricName()} */ @@ -191,7 +196,7 @@ public class MetricsProperties { } /** - * Set name of the metric for received requests. + * 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)} @@ -200,10 +205,6 @@ public class MetricsProperties { this.request.setMetricName(requestsMetricName); } - public ServerRequest getRequest() { - return this.request; - } - public int getMaxUriTags() { return this.maxUriTags; } @@ -220,12 +221,13 @@ public class MetricsProperties { private String metricName = "http.server.requests"; /** - * Automatically time requests. + * Auto-timed request settings. */ - private final AutoTime autoTime = new AutoTime(); + @NestedConfigurationProperty + private final Autotime autotime = new Autotime(); - public AutoTime getAutoTime() { - return this.autoTime; + public Autotime getAutotime() { + return this.autotime; } public String getMetricName() { @@ -240,56 +242,6 @@ public class MetricsProperties { } - public static class AutoTime { - - /** - * 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 enabled = true; - - /** - * Default percentiles when @Timed annotation is not presented on the - * corresponding request handler. Any @Timed annotation presented will have - * precedence. - */ - private List defaultPercentiles = new ArrayList<>(); - - /** - * Default histogram when @Timed annotation is not presented on the - * corresponding request handler. Any @Timed annotation presented will have - * precedence. - */ - private boolean defaultHistogram; - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public List getDefaultPercentiles() { - return this.defaultPercentiles; - } - - public void setDefaultPercentiles(List defaultPercentiles) { - this.defaultPercentiles = defaultPercentiles; - } - - public boolean isDefaultHistogram() { - return this.defaultHistogram; - } - - public void setDefaultHistogram(boolean defaultHistogram) { - this.defaultHistogram = defaultHistogram; - } - - } - } public static class Distribution { 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 d5d6d923ec4..d657e0a5d09 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 @@ -80,7 +80,7 @@ public class JerseyServerMetricsAutoConfiguration { Server server = this.properties.getWeb().getServer(); return (config) -> config.register(new MetricsApplicationEventListener( meterRegistry, tagsProvider, server.getRequest().getMetricName(), - server.getRequest().getAutoTime().isEnabled(), + server.getRequest().getAutotime().isEnabled(), new AnnotationUtilsAnnotationFinder())); } 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 7326b90743d..037fe32b8f3 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,8 +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.AutoTime; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Client; +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; @@ -55,13 +54,10 @@ class RestTemplateMetricsConfiguration { MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider restTemplateExchangeTagsProvider, MetricsProperties properties) { - - Client client = properties.getWeb().getClient(); - AutoTime autoTime = client.getRequest().getAutoTime(); + ClientRequest request = properties.getWeb().getClient().getRequest(); return new MetricsRestTemplateCustomizer(meterRegistry, - restTemplateExchangeTagsProvider, client.getRequest().getMetricName(), - autoTime.isEnabled(), autoTime.getDefaultPercentiles(), - autoTime.isDefaultHistogram()); + 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 5a88d721665..689772f0e0b 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().getRequest().getMetricName()); + 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 1d03e871227..fc9327ee325 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,8 +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.AutoTime; -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.reactive.server.DefaultWebFluxTagsProvider; @@ -67,11 +66,9 @@ public class WebFluxMetricsAutoConfiguration { @Bean public MetricsWebFilter webfluxMetrics(MeterRegistry registry, WebFluxTagsProvider tagConfigurer) { - Server serverProperties = this.properties.getWeb().getServer(); - AutoTime autotime = serverProperties.getRequest().getAutoTime(); - return new MetricsWebFilter(registry, tagConfigurer, - serverProperties.getRequest().getMetricName(), autotime.isEnabled(), - autotime.getDefaultPercentiles(), autotime.isDefaultHistogram()); + ServerRequest request = this.properties.getWeb().getServer().getRequest(); + return new MetricsWebFilter(registry, tagConfigurer, request.getMetricName(), + request.getAutotime()); } @Bean 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 4463fac6155..7f20381eadd 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,8 +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.AutoTime; -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; @@ -79,11 +78,9 @@ public class WebMvcMetricsAutoConfiguration { @Bean public FilterRegistrationBean webMvcMetricsFilter( MeterRegistry registry, WebMvcTagsProvider tagsProvider) { - Server serverProperties = this.properties.getWeb().getServer(); - AutoTime autotime = serverProperties.getRequest().getAutoTime(); + ServerRequest request = this.properties.getWeb().getServer().getRequest(); WebMvcMetricsFilter filter = new WebMvcMetricsFilter(registry, tagsProvider, - serverProperties.getRequest().getMetricName(), autotime.isEnabled(), - autotime.getDefaultPercentiles(), autotime.isDefaultHistogram()); + request.getMetricName(), request.getAutotime()); FilterRegistrationBean registration = new FilterRegistrationBean<>( filter); registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); 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 f853bd532c9..08a7529ec5a 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 40a5f02e676..ec61d507c74 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 ce92cedb7f2..8a7b1d858d3 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 6b15f1a95b7..d51fde76933 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.request.auto-time.enabled=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 898c499da73..1481fab1f70 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 @@ -141,14 +141,14 @@ public class WebMvcMetricsAutoConfigurationTests { } @Test - public void autoTimeRequestsDefaultValues() { + public void autoTimeRequestsCanBeConfigured() { this.contextRunner.withUserConfiguration(TestController.class) .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, WebMvcAutoConfiguration.class)) .withPropertyValues( - "management.metrics.web.server.request.auto-time.enabled=true", - "management.metrics.web.server.request.auto-time.default-percentiles=0.5,0.7", - "management.metrics.web.server.request.auto-time.default-histogram=true") + "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(); 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 00000000000..e02950a2ed0 --- /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 63c2a4d5535..d7469aca95c 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. @@ -18,13 +18,13 @@ package org.springframework.boot.actuate.metrics.web.client; import java.io.IOException; import java.net.URI; -import java.util.List; import java.util.Map; 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; @@ -51,11 +51,7 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto private final String metricName; - private final boolean autoTimeRequests; - - private final double[] percentiles; - - private final boolean histogram; + private final Autotime autotime; /** * Create a new {@code MetricsClientHttpRequestInterceptor}. @@ -63,11 +59,11 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto * @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, boolean, List, boolean)} + * {@link #MetricsClientHttpRequestInterceptor(MeterRegistry, RestTemplateExchangeTagsProvider, String, Autotime)} */ MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName) { - this(meterRegistry, tagProvider, metricName, true, null, false); + this(meterRegistry, tagProvider, metricName, new Autotime()); } /** @@ -75,25 +71,16 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto * @param meterRegistry the registry to which metrics are recorded * @param tagProvider provider for metrics tags * @param metricName name of the metric to record - * @param autoTimeRequests if requests should be automatically timed - * @param percentileList percentiles for auto time requests - * @param histogram histogram or not for auto time requests + * @param autotime auto timed request settings * @since 2.2.0 */ MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName, - boolean autoTimeRequests, List percentileList, boolean histogram) { - - double[] percentiles = (percentileList != null) - ? percentileList.stream().mapToDouble(Double::doubleValue).toArray() - : null; - + Autotime autotime) { this.tagProvider = tagProvider; this.meterRegistry = meterRegistry; this.metricName = metricName; - this.autoTimeRequests = autoTimeRequests; - this.percentiles = percentiles; - this.histogram = histogram; + this.autotime = (autotime != null) ? autotime : Autotime.disabled(); } @Override @@ -106,7 +93,7 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto return response; } finally { - if (this.autoTimeRequests) { + if (this.autotime.isEnabled()) { getTimeBuilder(request, response).register(this.meterRegistry) .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); } @@ -134,8 +121,9 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto private Timer.Builder getTimeBuilder(HttpRequest request, ClientHttpResponse response) { - return Timer.builder(this.metricName).publishPercentiles(this.percentiles) - .publishPercentileHistogram(this.histogram) + 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 ac3892db2f6..9efce605a53 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; @@ -46,32 +47,29 @@ public class MetricsRestTemplateCustomizer implements RestTemplateCustomizer { * @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, boolean, List, boolean)} + * {@link #MetricsRestTemplateCustomizer(MeterRegistry, RestTemplateExchangeTagsProvider, String, Autotime)} */ public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName) { - this.interceptor = new MetricsClientHttpRequestInterceptor(meterRegistry, - tagProvider, 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 - * {@code percentileList} and {@code histogram} configurations. + * {@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 autoTimeRequests if requests should be automatically timed - * @param percentileList percentiles for auto time requests - * @param histogram histogram or not for auto time requests + * @param autotime auto-timed request settings * @since 2.2.0 */ public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry, RestTemplateExchangeTagsProvider tagProvider, String metricName, - boolean autoTimeRequests, List percentileList, boolean histogram) { + Autotime autotime) { this.interceptor = new MetricsClientHttpRequestInterceptor(meterRegistry, - tagProvider, metricName, autoTimeRequests, percentileList, histogram); + 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 78d60852c8b..1f1984e4722 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 0fc99967f82..0943fe7f331 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. @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.metrics.web.reactive.client; -import java.util.List; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; @@ -24,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; @@ -48,11 +48,7 @@ public class MetricsWebClientFilterFunction implements ExchangeFilterFunction { private final String metricName; - private final boolean autoTimeRequests; - - private final double[] percentiles; - - private final boolean histogram; + private final Autotime autotime; /** * Create a new {@code MetricsWebClientFilterFunction}. @@ -60,11 +56,12 @@ public class MetricsWebClientFilterFunction implements ExchangeFilterFunction { * @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, boolean, List, boolean)} + * {@link #MetricsWebClientFilterFunction(MeterRegistry, WebClientExchangeTagsProvider, String, Autotime)} */ + @Deprecated public MetricsWebClientFilterFunction(MeterRegistry meterRegistry, WebClientExchangeTagsProvider tagProvider, String metricName) { - this(meterRegistry, tagProvider, metricName, true, null, false); + this(meterRegistry, tagProvider, metricName, new Autotime()); } /** @@ -72,40 +69,33 @@ public class MetricsWebClientFilterFunction implements ExchangeFilterFunction { * @param meterRegistry the registry to which metrics are recorded * @param tagProvider provider for metrics tags * @param metricName name of the metric to record - * @param autoTimeRequests if requests should be automatically timed - * @param percentileList percentiles for auto time requests - * @param histogram histogram or not for auto time requests + * @param autotime auto-timed request settings * @since 2.2.0 */ public MetricsWebClientFilterFunction(MeterRegistry meterRegistry, WebClientExchangeTagsProvider tagProvider, String metricName, - boolean autoTimeRequests, List percentileList, boolean histogram) { - - double[] percentiles = (percentileList != null) - ? percentileList.stream().mapToDouble(Double::doubleValue).toArray() - : null; - + Autotime autotime) { this.meterRegistry = meterRegistry; this.tagProvider = tagProvider; this.metricName = metricName; - this.autoTimeRequests = autoTimeRequests; - this.percentiles = percentiles; - this.histogram = histogram; + this.autotime = (autotime != null) ? autotime : Autotime.disabled(); } @Override public Mono filter(ClientRequest clientRequest, ExchangeFunction exchangeFunction) { return exchangeFunction.exchange(clientRequest).doOnEach((signal) -> { - if (!signal.isOnComplete() && this.autoTimeRequests) { + 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).publishPercentiles(this.percentiles) - .publishPercentileHistogram(this.histogram).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 6659a93025f..16861b014de 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 @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.metrics.web.reactive.server; -import java.util.List; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; @@ -25,6 +24,7 @@ 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; @@ -48,11 +48,7 @@ public class MetricsWebFilter implements WebFilter { private final String metricName; - private final boolean autoTimeRequests; - - private final double[] percentiles; - - private final boolean histogram; + private final Autotime autotime; /** * Create a new {@code MetricsWebFilter}. @@ -61,12 +57,13 @@ public class MetricsWebFilter implements WebFilter { * @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, boolean, List, boolean)} + * {@link #MetricsWebFilter(MeterRegistry, WebFluxTagsProvider, String, Autotime)} */ @Deprecated public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider, String metricName, boolean autoTimeRequests) { - this(registry, tagsProvider, metricName, autoTimeRequests, null, false); + this(registry, tagsProvider, metricName, + new Autotime(autoTimeRequests, false, null)); } /** @@ -74,30 +71,20 @@ public class MetricsWebFilter implements WebFilter { * @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 - * @param percentileList percentiles for auto time requests - * @param histogram histogram or not for auto time requests + * @param autotime auto timed request settings * @since 2.2.0 */ public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider, - String metricName, boolean autoTimeRequests, List percentileList, - boolean histogram) { - - double[] percentiles = (percentileList != null) - ? percentileList.stream().mapToDouble(Double::doubleValue).toArray() - : null; - + String metricName, Autotime autotime) { this.registry = registry; this.tagsProvider = tagsProvider; this.metricName = metricName; - this.autoTimeRequests = autoTimeRequests; - this.percentiles = percentiles; - this.histogram = histogram; + 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); @@ -122,8 +109,10 @@ public class MetricsWebFilter implements WebFilter { private void record(ServerWebExchange exchange, long start, Throwable cause) { Iterable tags = this.tagsProvider.httpRequestTags(exchange, cause); - Timer.builder(this.metricName).tags(tags).publishPercentiles(this.percentiles) - .publishPercentileHistogram(this.histogram).register(this.registry) + 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 05913ef0f4d..c210c779749 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 @@ -19,7 +19,6 @@ package org.springframework.boot.actuate.metrics.web.servlet; import java.io.IOException; import java.lang.reflect.AnnotatedElement; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.function.Supplier; @@ -35,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; @@ -60,11 +60,7 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { private final String metricName; - private final boolean autoTimeRequests; - - private final double[] autoTimeRequestsPercentiles; - - private final boolean autoTimeRequestsHistogram; + private final Autotime autotime; /** * Create a new {@link WebMvcMetricsFilter} instance. @@ -73,13 +69,14 @@ 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.1.4 in favor of - * {@link #WebMvcMetricsFilter(MeterRegistry, WebMvcTagsProvider, String, boolean, List, boolean)} + * @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, autoTimeRequests, null, false); + this(registry, tagsProvider, metricName, + new Autotime(autoTimeRequests, false, null)); } /** @@ -87,26 +84,15 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { * @param registry the meter registry * @param tagsProvider the tags provider * @param metricName the metric name - * @param autoTimeRequests if requests should be automatically timed - * @param autoTimeRequestsPercentiles default percentiles if requests are auto timed - * @param autoTimeRequestsHistogram default histogram flag if requests are auto timed + * @param autotime auto timed request settings * @since 2.2.0 */ public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider, - String metricName, boolean autoTimeRequests, - List autoTimeRequestsPercentiles, boolean autoTimeRequestsHistogram) { - - double[] percentiles = (autoTimeRequestsPercentiles != null) - ? autoTimeRequestsPercentiles.stream().mapToDouble(Double::doubleValue) - .toArray() - : null; - + String metricName, Autotime autotime) { this.registry = registry; this.tagsProvider = tagsProvider; this.metricName = metricName; - this.autoTimeRequests = autoTimeRequests; - this.autoTimeRequestsPercentiles = percentiles; - this.autoTimeRequestsHistogram = autoTimeRequestsHistogram; + this.autotime = autotime; } @Override @@ -187,10 +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) - .publishPercentiles(this.autoTimeRequestsPercentiles) - .publishPercentileHistogram(this.autoTimeRequestsHistogram)); + 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 1c7c01fc933..89369bf8eb7 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; @@ -59,7 +60,7 @@ public class MetricsRestTemplateCustomizerTests { this.mockServer = MockRestServiceServer.createServer(this.restTemplate); this.customizer = new MetricsRestTemplateCustomizer(this.registry, new DefaultRestTemplateExchangeTagsProvider(), "http.client.requests", - true, null, false); + 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 63ba7bb154a..8f82938b222 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 41475b12b0d..9c24fd2c3ab 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 fc09bce730f..dcae4bafcde 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,8 +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, null, - false); + 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 df84c90372f..03b5290f86b 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 @@ -30,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; @@ -106,7 +107,8 @@ public class WebMvcMetricsFilterAutoTimedTests { public WebMvcMetricsFilter webMetricsFilter(WebApplicationContext context, MeterRegistry registry) { return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(), - "http.server.requests", true, Arrays.asList(0.5, 0.95), 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 641af163101..f7ef2e1346a 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, null, false); + "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 a30abc38eff..06ae5ccb3ba 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, null, false); + "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 89734a0f200..3b310afbf8e 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.request.auto-time.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: +`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.request.auto-time.enabled` 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]