Extract AutoTimer interface for metrics

Refactor `Autotime` from a properties object to an interface and
change the existing metric recording implementations. The `AutoTimer`
interface is a general purpose callback that can be applied to a
`Timer.Builder` to configure it. Autotime properties are now located
in `spring-boot-actuator-autoconfigure` and have become an
implementation of the interface.

Closes gh-17026
pull/17010/head
Phillip Webb 6 years ago
parent ad5e905bd7
commit 68a3fbd7a0

@ -14,18 +14,21 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.metrics;
package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.List;
import io.micrometer.core.instrument.Timer.Builder;
import org.springframework.boot.actuate.metrics.AutoTimer;
/**
* Settings for requests that are automatically timed.
* Nested configuration properties for items that are automatically timed.
*
* @author Tadaya Tsuyukubo
* @author Stephane Nicoll
* @author Phillip Webb
* @since 2.2.0
*/
public final class Autotime {
public final class AutoTimeProperties implements AutoTimer {
private boolean enabled = true;
@ -36,24 +39,10 @@ public final class Autotime {
/**
* 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<Double> percentiles) {
this.enabled = enabled;
this.percentilesHistogram = percentilesHistogram;
this.percentiles = (percentiles != null)
? percentiles.stream().mapToDouble(Double::doubleValue).toArray() : null;
public AutoTimeProperties() {
}
@Override
public boolean isEnabled() {
return this.enabled;
}
@ -78,12 +67,10 @@ public final class Autotime {
this.percentiles = percentiles;
}
/**
* 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);
@Override
public void apply(Builder builder) {
builder.publishPercentileHistogram(this.percentilesHistogram)
.publishPercentiles(this.percentiles);
}
}

@ -19,7 +19,6 @@ package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
@ -116,6 +115,7 @@ public class MetricsProperties {
* @return request metric name
* @deprecated since 2.2.0 in favor of {@link ClientRequest#getMetricName()}
*/
@Deprecated
@DeprecatedConfigurationProperty(
replacement = "management.metrics.web.client.request.metric-name")
public String getRequestsMetricName() {
@ -128,6 +128,7 @@ public class MetricsProperties {
* @deprecated since 2.2.0 in favor of
* {@link ClientRequest#setMetricName(String)}
*/
@Deprecated
public void setRequestsMetricName(String requestsMetricName) {
this.request.setMetricName(requestsMetricName);
}
@ -151,10 +152,10 @@ public class MetricsProperties {
* Auto-timed request settings.
*/
@NestedConfigurationProperty
private final Autotime autoTime = new Autotime();
private final AutoTimeProperties autotime = new AutoTimeProperties();
public Autotime getAutotime() {
return this.autoTime;
public AutoTimeProperties getAutotime() {
return this.autotime;
}
public String getMetricName() {
@ -187,7 +188,7 @@ public class MetricsProperties {
/**
* Return whether server requests should be automatically timed.
* @return {@code true} if server request should be automatically timed
* @deprecated since 2.2.0 in favor of {@link Autotime#isEnabled()}
* @deprecated since 2.2.0 in favor of {@link AutoTimeProperties#isEnabled()}
*/
@DeprecatedConfigurationProperty(
replacement = "management.metrics.web.server.request.autotime.enabled")
@ -200,7 +201,7 @@ public class MetricsProperties {
* Set whether server requests should be automatically timed.
* @param autoTimeRequests whether server requests should be automatically
* timed
* @deprecated since 2.2.0 in favor of {@link Autotime#isEnabled()}
* @deprecated since 2.2.0 in favor of {@link AutoTimeProperties#isEnabled()}
*/
@Deprecated
public void setAutoTimeRequests(boolean autoTimeRequests) {
@ -249,9 +250,9 @@ public class MetricsProperties {
* Auto-timed request settings.
*/
@NestedConfigurationProperty
private final Autotime autotime = new Autotime();
private final AutoTimeProperties autotime = new AutoTimeProperties();
public Autotime getAutotime() {
public AutoTimeProperties getAutotime() {
return this.autotime;
}

@ -0,0 +1,100 @@
/*
* 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;
/**
* @author pwebb
*/
import java.util.function.Supplier;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.Timer.Builder;
/**
* Strategy that can be used to apply {@link Timer Timers} automatically instead of using
* {@link Timed @Timed}.
*
* @author Tadaya Tsuyukubo
* @author Stephane Nicoll
* @author Phillip Webb
* @since 2.2.0
*/
@FunctionalInterface
public interface AutoTimer {
/**
* An {@link AutoTimer} implementation that is enabled but applies no additional
* customizations.
*/
AutoTimer ENABLED = (builder) -> {
};
/**
* An {@link AutoTimer} implementation that is disabled and will not record metrics.
*/
AutoTimer DISABLED = new AutoTimer() {
@Override
public boolean isEnabled() {
return false;
}
@Override
public void apply(Builder builder) {
throw new IllegalStateException("AutoTimer is disabled");
}
};
/**
* Return if the auto-timer is enabled and metrics should be recorded.
* @return if the auto-timer is enabled
*/
default boolean isEnabled() {
return true;
}
/**
* Factory method to create a new {@link Builder Timer.Builder} with auto-timer
* settings {@link #apply(Builder) applied}.
* @param name the name of the timer
* @return a new builder instance with auto-settings applied
*/
default Timer.Builder builder(String name) {
return builder(() -> Timer.builder(name));
}
/**
* Factory method to create a new {@link Builder Timer.Builder} with auto-timer
* settings {@link #apply(Builder) applied}.
* @param supplier the builder supplier
* @return a new builder instance with auto-settings applied
*/
default Timer.Builder builder(Supplier<Timer.Builder> supplier) {
Timer.Builder builder = supplier.get();
apply(builder);
return builder;
}
/**
* Called to apply any auto-timer settings to the given {@link Builder Timer.Builder}.
* @param builder the builder to apply settings to
*/
void apply(Timer.Builder builder);
}

@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
@ -51,7 +51,7 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto
private final String metricName;
private final Autotime autotime;
private final AutoTimer autoTimer;
/**
* Create a new {@code MetricsClientHttpRequestInterceptor}.
@ -59,11 +59,12 @@ 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, Autotime)}
* {@link #MetricsClientHttpRequestInterceptor(MeterRegistry, RestTemplateExchangeTagsProvider, String, AutoTimer)}
*/
@Deprecated
MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry,
RestTemplateExchangeTagsProvider tagProvider, String metricName) {
this(meterRegistry, tagProvider, metricName, new Autotime());
this(meterRegistry, tagProvider, metricName, AutoTimer.ENABLED);
}
/**
@ -71,21 +72,24 @@ 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 autotime auto timed request settings
* @param autoTimer the auto-timers to apply or {@code null} to disable auto-timing
* @since 2.2.0
*/
MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry,
RestTemplateExchangeTagsProvider tagProvider, String metricName,
Autotime autotime) {
AutoTimer autoTimer) {
this.tagProvider = tagProvider;
this.meterRegistry = meterRegistry;
this.metricName = metricName;
this.autotime = (autotime != null) ? autotime : Autotime.disabled();
this.autoTimer = (autoTimer != null) ? autoTimer : AutoTimer.DISABLED;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
if (!this.autoTimer.isEnabled()) {
return execution.execute(request, body);
}
long startTime = System.nanoTime();
ClientHttpResponse response = null;
try {
@ -93,10 +97,8 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto
return response;
}
finally {
if (this.autotime.isEnabled()) {
getTimeBuilder(request, response).register(this.meterRegistry)
.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
}
urlTemplate.remove();
}
}
@ -121,9 +123,7 @@ class MetricsClientHttpRequestInterceptor implements ClientHttpRequestIntercepto
private Timer.Builder getTimeBuilder(HttpRequest request,
ClientHttpResponse response) {
return Timer.builder(this.metricName)
.publishPercentiles(this.autotime.getPercentiles())
.publishPercentileHistogram(this.autotime.isPercentilesHistogram())
return this.autoTimer.builder(this.metricName)
.tags(this.tagProvider.getTags(urlTemplate.get(), request, response))
.description("Timer of RestTemplate operation");
}

@ -21,7 +21,7 @@ import java.util.List;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
@ -47,29 +47,30 @@ 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, Autotime)}
* {@link #MetricsRestTemplateCustomizer(MeterRegistry, RestTemplateExchangeTagsProvider, String, AutoTimer)}
*/
@Deprecated
public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry,
RestTemplateExchangeTagsProvider tagProvider, String metricName) {
this(meterRegistry, tagProvider, metricName, new Autotime());
this(meterRegistry, tagProvider, metricName, AutoTimer.ENABLED);
}
/**
* Creates a new {@code MetricsRestTemplateInterceptor}. When {@code autoTimeRequests}
* is set to {@code true}, the interceptor records metrics using the given
* {@code meterRegistry} with tags provided by the given {@code tagProvider} and with
* {@link Autotime auto-timed request configuration}.
* {@link AutoTimer auto-timed configuration}.
* @param meterRegistry the meter registry
* @param tagProvider the tag provider
* @param metricName the name of the recorded metric
* @param autotime auto-timed request settings
* @param autoTimer the auto-timers to apply or {@code null} to disable auto-timing
* @since 2.2.0
*/
public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry,
RestTemplateExchangeTagsProvider tagProvider, String metricName,
Autotime autotime) {
AutoTimer autoTimer) {
this.interceptor = new MetricsClientHttpRequestInterceptor(meterRegistry,
tagProvider, metricName, autotime);
tagProvider, metricName, autoTimer);
}
@Override

@ -18,7 +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.actuate.metrics.AutoTimer;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
import org.springframework.web.reactive.function.client.WebClient;
@ -41,12 +41,12 @@ public class MetricsWebClientCustomizer implements WebClientCustomizer {
* @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)}
* {@link #MetricsWebClientCustomizer(MeterRegistry, WebClientExchangeTagsProvider, String, AutoTimer)}
*/
@Deprecated
public MetricsWebClientCustomizer(MeterRegistry meterRegistry,
WebClientExchangeTagsProvider tagProvider, String metricName) {
this(meterRegistry, tagProvider, metricName, new Autotime());
this(meterRegistry, tagProvider, metricName, AutoTimer.ENABLED);
}
/**
@ -56,14 +56,14 @@ public class MetricsWebClientCustomizer implements WebClientCustomizer {
* @param meterRegistry the meter registry
* @param tagProvider the tag provider
* @param metricName the name of the recorded metric
* @param autotime auto-timed request settings
* @param autoTimer the auto-timers to apply or {@code null} to disable auto-timing
* @since 2.2.0
*/
public MetricsWebClientCustomizer(MeterRegistry meterRegistry,
WebClientExchangeTagsProvider tagProvider, String metricName,
Autotime autotime) {
AutoTimer autoTimer) {
this.filterFunction = new MetricsWebClientFilterFunction(meterRegistry,
tagProvider, metricName, autotime);
tagProvider, metricName, autoTimer);
}
@Override

@ -20,10 +20,10 @@ import java.util.concurrent.TimeUnit;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
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,7 +48,7 @@ public class MetricsWebClientFilterFunction implements ExchangeFilterFunction {
private final String metricName;
private final Autotime autotime;
private final AutoTimer autoTimer;
/**
* Create a new {@code MetricsWebClientFilterFunction}.
@ -56,12 +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, Autotime)}
* {@link #MetricsWebClientFilterFunction(MeterRegistry, WebClientExchangeTagsProvider, String, AutoTimer)}
*/
@Deprecated
public MetricsWebClientFilterFunction(MeterRegistry meterRegistry,
WebClientExchangeTagsProvider tagProvider, String metricName) {
this(meterRegistry, tagProvider, metricName, new Autotime());
this(meterRegistry, tagProvider, metricName, AutoTimer.ENABLED);
}
/**
@ -69,38 +69,43 @@ 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 autotime auto-timed request settings
* @param autoTimer the auto-timer configuration or {@code null} to disable
* @since 2.2.0
*/
public MetricsWebClientFilterFunction(MeterRegistry meterRegistry,
WebClientExchangeTagsProvider tagProvider, String metricName,
Autotime autotime) {
AutoTimer autoTimer) {
this.meterRegistry = meterRegistry;
this.tagProvider = tagProvider;
this.metricName = metricName;
this.autotime = (autotime != null) ? autotime : Autotime.disabled();
this.autoTimer = (autoTimer != null) ? autoTimer : AutoTimer.DISABLED;
}
@Override
public Mono<ClientResponse> filter(ClientRequest clientRequest,
ExchangeFunction exchangeFunction) {
return exchangeFunction.exchange(clientRequest).doOnEach((signal) -> {
if (!signal.isOnComplete() && this.autotime.isEnabled()) {
Long startTime = signal.getContext().get(METRICS_WEBCLIENT_START_TIME);
ClientResponse clientResponse = signal.get();
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
if (!this.autoTimer.isEnabled()) {
return next.exchange(request);
}
return next.exchange(request).doOnEach((signal) -> {
if (!signal.isOnComplete()) {
Long startTime = getStartTime(signal.getContext());
ClientResponse response = signal.get();
Throwable throwable = signal.getThrowable();
Iterable<Tag> tags = this.tagProvider.tags(clientRequest, clientResponse,
throwable);
Timer.builder(this.metricName)
.publishPercentiles(this.autotime.getPercentiles())
.publishPercentileHistogram(
this.autotime.isPercentilesHistogram())
.tags(tags).description("Timer of WebClient operation")
Iterable<Tag> tags = this.tagProvider.tags(request, response, throwable);
this.autoTimer.builder(this.metricName).tags(tags)
.description("Timer of WebClient operation")
.register(this.meterRegistry)
.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
}
}).subscriberContext((context) -> context.put(METRICS_WEBCLIENT_START_TIME,
System.nanoTime()));
}).subscriberContext(this::putStartTime);
}
private Long getStartTime(Context context) {
return context.get(METRICS_WEBCLIENT_START_TIME);
}
private Context putStartTime(Context context) {
return context.put(METRICS_WEBCLIENT_START_TIME, System.nanoTime());
}
}

@ -20,11 +20,10 @@ import java.util.concurrent.TimeUnit;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpResponse;
@ -48,7 +47,7 @@ public class MetricsWebFilter implements WebFilter {
private final String metricName;
private final Autotime autotime;
private final AutoTimer autoTimer;
/**
* Create a new {@code MetricsWebFilter}.
@ -57,13 +56,12 @@ 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, Autotime)}
* {@link #MetricsWebFilter(MeterRegistry, WebFluxTagsProvider, String, AutoTimer)}
*/
@Deprecated
public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
String metricName, boolean autoTimeRequests) {
this(registry, tagsProvider, metricName,
new Autotime(autoTimeRequests, false, null));
this(registry, tagsProvider, metricName, AutoTimer.ENABLED);
}
/**
@ -71,30 +69,37 @@ 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 autotime auto timed request settings
* @param autoTimer the auto-timers to apply or {@code null} to disable auto-timing
* @since 2.2.0
*/
public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
String metricName, Autotime autotime) {
String metricName, AutoTimer autoTimer) {
this.registry = registry;
this.tagsProvider = tagsProvider;
this.metricName = metricName;
this.autotime = (autotime != null) ? autotime : Autotime.disabled();
this.autoTimer = (autoTimer != null) ? autoTimer : AutoTimer.DISABLED;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
if (this.autotime.isEnabled()) {
return chain.filter(exchange).compose((call) -> filter(exchange, call));
}
if (!this.autoTimer.isEnabled()) {
return chain.filter(exchange);
}
return chain.filter(exchange).compose((call) -> filter(exchange, call));
}
private Publisher<Void> filter(ServerWebExchange exchange, Mono<Void> call) {
long start = System.nanoTime();
return call.doOnSuccess((done) -> onSuccess(exchange, start))
.doOnError((cause) -> onError(exchange, start, cause));
}
private void onSuccess(ServerWebExchange exchange, long start) {
record(exchange, start, null);
}
private void onError(ServerWebExchange exchange, long start, Throwable cause) {
ServerHttpResponse response = exchange.getResponse();
return call.doOnSuccess((done) -> record(exchange, start, null))
.doOnError((cause) -> {
if (response.isCommitted()) {
record(exchange, start, cause);
}
@ -104,15 +109,11 @@ public class MetricsWebFilter implements WebFilter {
return Mono.empty();
});
}
});
}
private void record(ServerWebExchange exchange, long start, Throwable cause) {
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, cause);
Timer.builder(this.metricName).tags(tags)
.publishPercentiles(this.autotime.getPercentiles())
.publishPercentileHistogram(this.autotime.isPercentilesHistogram())
.register(this.registry)
this.autoTimer.builder(this.metricName).tags(tags).register(this.registry)
.record(System.nanoTime() - start, TimeUnit.NANOSECONDS);
}

@ -20,7 +20,6 @@ import java.io.IOException;
import java.lang.reflect.AnnotatedElement;
import java.util.Collections;
import java.util.Set;
import java.util.function.Supplier;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@ -29,12 +28,11 @@ import javax.servlet.http.HttpServletResponse;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
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.boot.actuate.metrics.AutoTimer;
import org.springframework.core.annotation.MergedAnnotationCollectors;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.http.HttpStatus;
@ -60,7 +58,7 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
private final String metricName;
private final Autotime autotime;
private final AutoTimer autoTimer;
/**
* Create a new {@link WebMvcMetricsFilter} instance.
@ -70,13 +68,12 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
* @param autoTimeRequests if requests should be automatically timed
* @since 2.0.7
* @deprecated since 2.2.0 in favor of
* {@link #WebMvcMetricsFilter(MeterRegistry, WebMvcTagsProvider, String, Autotime)}
* {@link #WebMvcMetricsFilter(MeterRegistry, WebMvcTagsProvider, String, AutoTimer)}
*/
@Deprecated
public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider,
String metricName, boolean autoTimeRequests) {
this(registry, tagsProvider, metricName,
new Autotime(autoTimeRequests, false, null));
this(registry, tagsProvider, metricName, AutoTimer.ENABLED);
}
/**
@ -84,15 +81,15 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
* @param registry the meter registry
* @param tagsProvider the tags provider
* @param metricName the metric name
* @param autotime auto timed request settings
* @param autoTimer the auto-timers to apply or {@code null} to disable auto-timing
* @since 2.2.0
*/
public WebMvcMetricsFilter(MeterRegistry registry, WebMvcTagsProvider tagsProvider,
String metricName, Autotime autotime) {
String metricName, AutoTimer autoTimer) {
this.registry = registry;
this.tagsProvider = tagsProvider;
this.metricName = metricName;
this.autotime = autotime;
this.autoTimer = autoTimer;
}
@Override
@ -104,12 +101,6 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
filterAndRecordMetrics(request, response, filterChain);
}
private void filterAndRecordMetrics(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
TimingContext timingContext = TimingContext.get(request);
if (timingContext == null) {
timingContext = startAndAttachTimingContext(request);
@ -123,16 +114,16 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
// TimingContext that was attached to the first)
Throwable exception = (Throwable) request
.getAttribute(DispatcherServlet.EXCEPTION_ATTRIBUTE);
record(timingContext, response, request, exception);
record(timingContext, request, response, exception);
}
}
catch (NestedServletException ex) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
record(timingContext, response, request, ex.getCause());
record(timingContext, request, response, ex.getCause());
throw ex;
}
catch (ServletException | IOException | RuntimeException ex) {
record(timingContext, response, request, ex);
record(timingContext, request, response, ex);
throw ex;
}
}
@ -144,6 +135,26 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
return timingContext;
}
private void record(TimingContext timingContext, HttpServletRequest request,
HttpServletResponse response, Throwable exception) {
Object handler = getHandler(request);
Set<Timed> annotations = getTimedAnnotations(handler);
Timer.Sample timerSample = timingContext.getTimerSample();
if (annotations.isEmpty()) {
Builder builder = this.autoTimer.builder(this.metricName);
timerSample.stop(getTimer(builder, handler, request, response, exception));
return;
}
for (Timed annotation : annotations) {
Builder builder = Timer.builder(annotation, this.metricName);
timerSample.stop(getTimer(builder, handler, request, response, exception));
}
}
private Object getHandler(HttpServletRequest request) {
return request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
}
private Set<Timed> getTimedAnnotations(Object handler) {
if (!(handler instanceof HandlerMethod)) {
return Collections.emptySet();
@ -152,45 +163,27 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter {
}
private Set<Timed> getTimedAnnotations(HandlerMethod handler) {
Set<Timed> timed = findTimedAnnotations(handler.getMethod());
if (timed.isEmpty()) {
return findTimedAnnotations(handler.getBeanType());
Set<Timed> methodAnnotations = findTimedAnnotations(handler.getMethod());
if (!methodAnnotations.isEmpty()) {
return methodAnnotations;
}
return timed;
return findTimedAnnotations(handler.getBeanType());
}
private Set<Timed> findTimedAnnotations(AnnotatedElement element) {
return MergedAnnotations.from(element).stream(Timed.class)
.collect(MergedAnnotationCollectors.toAnnotationSet());
}
private void record(TimingContext timingContext, HttpServletResponse response,
HttpServletRequest request, Throwable exception) {
Object handlerObject = request
.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
Set<Timed> annotations = getTimedAnnotations(handlerObject);
Timer.Sample timerSample = timingContext.getTimerSample();
Supplier<Iterable<Tag>> tags = () -> this.tagsProvider.getTags(request, response,
handlerObject, exception);
if (annotations.isEmpty()) {
if (this.autotime.isEnabled()) {
stop(timerSample, tags,
Timer.builder(this.metricName)
.publishPercentiles(this.autotime.getPercentiles())
.publishPercentileHistogram(
this.autotime.isPercentilesHistogram()));
}
}
else {
for (Timed annotation : annotations) {
stop(timerSample, tags, Timer.builder(annotation, this.metricName));
}
MergedAnnotations annotations = MergedAnnotations.from(element);
if (!annotations.isPresent(Timed.class)) {
return Collections.emptySet();
}
return annotations.stream(Timed.class)
.collect(MergedAnnotationCollectors.toAnnotationSet());
}
private void stop(Timer.Sample timerSample, Supplier<Iterable<Tag>> tags,
Builder builder) {
timerSample.stop(builder.tags(tags.get()).register(this.registry));
private Timer getTimer(Builder builder, Object handler, HttpServletRequest request,
HttpServletResponse response, Throwable exception) {
return builder
.tags(this.tagsProvider.getTags(request, response, handler, exception))
.register(this.registry);
}
/**

@ -27,7 +27,7 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
@ -60,7 +60,7 @@ public class MetricsRestTemplateCustomizerTests {
this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
this.customizer = new MetricsRestTemplateCustomizer(this.registry,
new DefaultRestTemplateExchangeTagsProvider(), "http.client.requests",
new Autotime());
AutoTimer.ENABLED);
this.customizer.customize(this.restTemplate);
}

@ -30,7 +30,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientRequest;
@ -65,7 +65,7 @@ public class MetricsWebClientFilterFunctionTests {
this.registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock());
this.filterFunction = new MetricsWebClientFilterFunction(this.registry,
new DefaultWebClientExchangeTagsProvider(), "http.client.requests",
new Autotime());
AutoTimer.ENABLED);
this.response = mock(ClientResponse.class);
this.exchange = (r) -> Mono.just(this.response);
}

@ -25,7 +25,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.reactive.HandlerMapping;
@ -51,7 +51,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, new Autotime());
new DefaultWebFluxTagsProvider(), REQUEST_METRICS_NAME,
AutoTimer.ENABLED);
}
@Test

@ -16,8 +16,6 @@
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;
@ -30,7 +28,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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;
@ -108,7 +105,8 @@ public class WebMvcMetricsFilterAutoTimedTests {
MeterRegistry registry) {
return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(),
"http.server.requests",
new Autotime(true, true, Arrays.asList(0.5, 0.95)));
(builder) -> builder.publishPercentiles(0.5, 0.95)
.publishPercentileHistogram(true));
}
}

@ -56,7 +56,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@ -373,7 +373,7 @@ public class WebMvcMetricsFilterTests {
WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry,
WebApplicationContext ctx) {
return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(),
"http.server.requests", new Autotime());
"http.server.requests", AutoTimer.ENABLED);
}
}

@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.Autotime;
import org.springframework.boot.actuate.metrics.AutoTimer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@ -112,7 +112,7 @@ public class WebMvcMetricsIntegrationTests {
public WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry,
WebApplicationContext ctx) {
return new WebMvcMetricsFilter(registry, new DefaultWebMvcTagsProvider(),
"http.server.requests", new Autotime());
"http.server.requests", AutoTimer.ENABLED);
}
@Configuration(proxyBeanMethods = false)

Loading…
Cancel
Save