Commit 4df6f5de authored by Tadaya Tsuyukubo's avatar Tadaya Tsuyukubo Committed by Stephane Nicoll

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
parent 3d814a52
......@@ -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<Double> 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<Double> getDefaultPercentiles() {
return this.defaultPercentiles;
}
public void setRequestsMetricName(String requestsMetricName) {
this.requestsMetricName = requestsMetricName;
public void setDefaultPercentiles(List<Double> 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;
}
}
......
......@@ -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",
......
......@@ -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));
......
......@@ -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());
}
}
......@@ -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());
}
}
......@@ -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",
......
......@@ -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> 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<WebMvcMetricsFilter> 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",
......
......@@ -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();
......
......@@ -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() {
......
......@@ -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<Double> 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");
}
......
......@@ -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<Double> percentileList, boolean histogram) {
this.interceptor = new MetricsClientHttpRequestInterceptor(meterRegistry,
tagProvider, metricName, autoTimeRequests, percentileList, histogram);
}
@Override
public void customize(RestTemplate restTemplate) {
UriTemplateHandler templateHandler = restTemplate.getUriTemplateHandler();
......
......@@ -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<Double> 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<ClientResponse> 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<Tag> 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);
......
......@@ -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<Double> 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<Void> filter(ServerWebExchange exchange, Mono<Void> 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<Tag> 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<Tag> 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);
}
}
......@@ -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<Double> 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 {
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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);
}
}
......
......@@ -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);
}
}
......
......@@ -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)
......
......@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment