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-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/AutoTimeProperties.java similarity index 58% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Autotime.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/AutoTimeProperties.java index 6c73fe3acd4..0af123747a1 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Autotime.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/AutoTimeProperties.java @@ -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 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); } } 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 889f7c8a17c..add171327a0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java @@ -19,7 +19,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; } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/AutoTimer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/AutoTimer.java new file mode 100644 index 00000000000..31c27776cbc --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/AutoTimer.java @@ -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 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); + +} 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 d7469aca95c..6962e25e8c2 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 @@ -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); - } + 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"); } 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 9efce605a53..2688df056e9 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 @@ -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 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 47a0fabfe24..4e64f7d8fdc 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,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 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 0943fe7f331..3c4692cecc4 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 @@ -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 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 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 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 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()); } } 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 16861b014de..5ed64c563f6 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java @@ -20,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,48 +69,51 @@ 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 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); + return chain.filter(exchange).compose((call) -> filter(exchange, call)); } private Publisher filter(ServerWebExchange exchange, Mono 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); - } - else { - response.beforeCommit(() -> { - record(exchange, start, cause); - return Mono.empty(); - }); - } - }); + if (response.isCommitted()) { + record(exchange, start, cause); + } + else { + response.beforeCommit(() -> { + record(exchange, start, cause); + return Mono.empty(); + }); + } } private void record(ServerWebExchange exchange, long start, Throwable cause) { Iterable 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); } 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 c210c779749..645e95584bc 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 @@ -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 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 getTimedAnnotations(Object handler) { if (!(handler instanceof HandlerMethod)) { return Collections.emptySet(); @@ -152,45 +163,27 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { } private Set getTimedAnnotations(HandlerMethod handler) { - Set timed = findTimedAnnotations(handler.getMethod()); - if (timed.isEmpty()) { - return findTimedAnnotations(handler.getBeanType()); + Set methodAnnotations = findTimedAnnotations(handler.getMethod()); + if (!methodAnnotations.isEmpty()) { + return methodAnnotations; } - return timed; + return findTimedAnnotations(handler.getBeanType()); } private Set findTimedAnnotations(AnnotatedElement element) { - return MergedAnnotations.from(element).stream(Timed.class) + MergedAnnotations annotations = MergedAnnotations.from(element); + if (!annotations.isPresent(Timed.class)) { + return Collections.emptySet(); + } + return annotations.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 annotations = getTimedAnnotations(handlerObject); - Timer.Sample timerSample = timingContext.getTimerSample(); - Supplier> 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)); - } - } - } - - private void stop(Timer.Sample timerSample, Supplier> 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); } /** 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 08920513128..2c9ed173105 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,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); } 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 65b4fdf1bd5..2f8a165955e 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,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); } 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 c94b4a88b46..3a902b7d4ed 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 @@ -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 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 fcc48a9ac2c..31d57ea08d9 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,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)); } } 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 c6683a429e7..d029de1d11f 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,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); } } 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 7145a929924..c7469e517b0 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,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)