diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java index c3e483d769..c7af8e8096 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfiguration.java @@ -27,7 +27,6 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; @@ -111,12 +110,12 @@ public class WebFluxObservationAutoConfiguration { MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties, ObservationProperties observationProperties) { String observationName = observationProperties.getHttp().getServer().getRequests().getName(); - Server metricsServerProperties = metricsProperties.getWeb().getServer(); - String metricName = metricsServerProperties.getRequest().getMetricName(); - String name = (observationName != null) ? observationName : metricName; + String name = (observationName != null) ? observationName + : metricsProperties.getWeb().getServer().getRequest().getMetricName(); MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter( () -> "Reached the maximum number of URI tags for '%s'.".formatted(name)); - return MeterFilter.maximumAllowableTags(name, "uri", metricsServerProperties.getMaxUriTags(), filter); + return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(), + filter); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfiguration.java index 1188d42b3e..0c34fd27c7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfiguration.java @@ -84,10 +84,7 @@ public class WebMvcObservationAutoConfiguration { public FilterRegistrationBean webMvcObservationFilter(ObservationRegistry registry, ObjectProvider customTagsProvider, ObjectProvider contributorsProvider) { - - String observationName = this.observationProperties.getHttp().getServer().getRequests().getName(); - String metricName = this.metricsProperties.getWeb().getServer().getRequest().getMetricName(); - String name = (observationName != null) ? observationName : metricName; + String name = httpRequestsMetricName(this.observationProperties, this.metricsProperties); ServerRequestObservationConvention convention = new DefaultServerRequestObservationConvention(name); WebMvcTagsProvider tagsProvider = customTagsProvider.getIfAvailable(); List contributors = contributorsProvider.orderedStream().toList(); @@ -101,6 +98,13 @@ public class WebMvcObservationAutoConfiguration { return registration; } + private static String httpRequestsMetricName(ObservationProperties observationProperties, + MetricsProperties metricsProperties) { + String observationName = observationProperties.getHttp().getServer().getRequests().getName(); + return (observationName != null) ? observationName + : metricsProperties.getWeb().getServer().getRequest().getMetricName(); + } + @Configuration(proxyBeanMethods = false) @ConditionalOnClass(MeterRegistry.class) @ConditionalOnBean(MeterRegistry.class) @@ -110,9 +114,7 @@ public class WebMvcObservationAutoConfiguration { @Order(0) MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties, ObservationProperties observationProperties) { - String observationName = observationProperties.getHttp().getServer().getRequests().getName(); - String metricName = metricsProperties.getWeb().getServer().getRequest().getMetricName(); - String name = (observationName != null) ? observationName : metricName; + String name = httpRequestsMetricName(observationProperties, metricsProperties); MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter( () -> String.format("Reached the maximum number of URI tags for '%s'.", name)); return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(), diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java index 6cb1ac4d5d..b71a80a66e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/reactive/WebFluxObservationAutoConfigurationTests.java @@ -16,21 +16,28 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.reactive; +import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; +import org.springframework.boot.actuate.autoconfigure.metrics.web.TestController; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.actuate.metrics.web.reactive.server.DefaultWebFluxTagsProvider; import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsContributor; import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsProvider; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; +import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.filter.reactive.ServerHttpObservationFilter; import org.springframework.web.server.ServerWebExchange; @@ -76,6 +83,75 @@ class WebFluxObservationAutoConfigurationTests { }); } + @Test + void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) { + this.contextRunner.withUserConfiguration(TestController.class) + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, + ObservationAutoConfiguration.class, WebFluxAutoConfiguration.class)) + .withPropertyValues("management.metrics.web.server.max-uri-tags=2").run((context) -> { + MeterRegistry registry = getInitializedMeterRegistry(context); + assertThat(registry.get("http.server.requests").meters().size()).isLessThanOrEqualTo(2); + assertThat(output).contains("Reached the maximum number of URI tags for 'http.server.requests'"); + }); + } + + @Test + @Deprecated(since = "3.0.0", forRemoval = true) + void afterMaxUrisReachedFurtherUrisAreDeniedWhenUsingCustomMetricName(CapturedOutput output) { + this.contextRunner.withUserConfiguration(TestController.class) + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, + ObservationAutoConfiguration.class, WebFluxAutoConfiguration.class)) + .withPropertyValues("management.metrics.web.server.max-uri-tags=2", + "management.metrics.web.server.request.metric-name=my.http.server.requests") + .run((context) -> { + MeterRegistry registry = getInitializedMeterRegistry(context); + assertThat(registry.get("my.http.server.requests").meters().size()).isLessThanOrEqualTo(2); + assertThat(output).contains("Reached the maximum number of URI tags for 'my.http.server.requests'"); + }); + } + + @Test + void afterMaxUrisReachedFurtherUrisAreDeniedWhenUsingCustomObservationName(CapturedOutput output) { + this.contextRunner.withUserConfiguration(TestController.class) + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, + ObservationAutoConfiguration.class, WebFluxAutoConfiguration.class)) + .withPropertyValues("management.metrics.web.server.max-uri-tags=2", + "management.observations.http.server.requests.name=my.http.server.requests") + .run((context) -> { + MeterRegistry registry = getInitializedMeterRegistry(context); + assertThat(registry.get("my.http.server.requests").meters().size()).isLessThanOrEqualTo(2); + assertThat(output).contains("Reached the maximum number of URI tags for 'my.http.server.requests'"); + }); + } + + @Test + void shouldNotDenyNorLogIfMaxUrisIsNotReached(CapturedOutput output) { + this.contextRunner.withUserConfiguration(TestController.class) + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, + ObservationAutoConfiguration.class, WebFluxAutoConfiguration.class)) + .withPropertyValues("management.metrics.web.server.max-uri-tags=5").run((context) -> { + MeterRegistry registry = getInitializedMeterRegistry(context); + assertThat(registry.get("http.server.requests").meters()).hasSize(3); + assertThat(output) + .doesNotContain("Reached the maximum number of URI tags for 'http.server.requests'"); + }); + } + + private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context) + throws Exception { + return getInitializedMeterRegistry(context, "/test0", "/test1", "/test2"); + } + + private MeterRegistry getInitializedMeterRegistry(AssertableReactiveWebApplicationContext context, String... urls) + throws Exception { + assertThat(context).hasSingleBean(ServerHttpObservationFilter.class); + WebTestClient client = WebTestClient.bindToApplicationContext(context).build(); + for (String url : urls) { + client.get().uri(url).exchange().expectStatus().isOk(); + } + return context.getBean(MeterRegistry.class); + } + @Deprecated(since = "3.0.0", forRemoval = true) @Configuration(proxyBeanMethods = false) static class CustomTagsProviderConfiguration { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfigurationTests.java index 297d862d9f..ae069ebfcc 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfigurationTests.java @@ -149,6 +149,7 @@ class WebMvcObservationAutoConfigurationTests { } @Test + @Deprecated(since = "3.0.0", forRemoval = true) void afterMaxUrisReachedFurtherUrisAreDeniedWhenUsingCustomMetricName(CapturedOutput output) { this.contextRunner.withUserConfiguration(TestController.class) .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class,