From 6f05c190e13976c083393b38ea47bd649f5d1190 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 29 May 2025 15:25:38 +0100 Subject: [PATCH] Move HTTP client observation support into relevant modules --- ...tpClientObservationsAutoConfiguration.java | 82 ------------------- ...ot.autoconfigure.AutoConfiguration.imports | 1 - .../metrics/test/MetricsIntegrationTests.java | 4 +- .../spring-boot-http-client/build.gradle | 2 + .../HttpClientMetricsAutoConfiguration.java | 62 ++++++++++++++ .../autoconfigure/metrics}/package-info.java | 4 +- ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...tpClientMetricsAutoConfigurationTests.java | 60 ++++++++++++++ .../spring-boot-restclient/build.gradle | 2 + ...stClientObservationAutoConfiguration.java} | 18 ++-- ...TemplateObservationAutoConfiguration.java} | 18 ++-- .../observation/package-info.java | 20 +++++ .../ObservationRestClientCustomizer.java | 2 +- .../ObservationRestTemplateCustomizer.java | 2 +- .../observation/package-info.java | 2 +- ...ot.autoconfigure.AutoConfiguration.imports | 4 +- ...entObservationAutoConfigurationTests.java} | 47 +++-------- ...AutoConfigurationWithoutMetricsTests.java} | 15 ++-- ...ateObservationAutoConfigurationTests.java} | 39 ++------- ...AutoConfigurationWithoutMetricsTests.java} | 8 +- .../ObservationRestClientCustomizerTests.java | 2 +- ...bservationRestTemplateCustomizerTests.java | 2 +- .../spring-boot-webclient/build.gradle | 1 + ...ebClientObservationAutoConfiguration.java} | 20 +++-- .../observation/package-info.java | 20 +++++ .../ObservationWebClientCustomizer.java | 2 +- .../observation/package-info.java | 2 +- ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...entObservationAutoConfigurationTests.java} | 59 ++----------- .../ObservationWebClientCustomizerTests.java | 2 +- 30 files changed, 253 insertions(+), 251 deletions(-) delete mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/HttpClientObservationsAutoConfiguration.java create mode 100644 spring-boot-project/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfiguration.java rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client => spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics}/package-info.java (82%) create mode 100644 spring-boot-project/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfigurationTests.java rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfiguration.java => spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfiguration.java} (77%) rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfiguration.java => spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfiguration.java} (77%) create mode 100644 spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/package-info.java rename spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/{actuate => }/observation/ObservationRestClientCustomizer.java (97%) rename spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/{actuate => }/observation/ObservationRestTemplateCustomizer.java (96%) rename spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/{actuate => }/observation/package-info.java (91%) rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationTests.java => spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationTests.java} (74%) rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationWithoutMetricsTests.java => spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationWithoutMetricsTests.java} (82%) rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationTests.java => spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationTests.java} (76%) rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationWithoutMetricsTests.java => spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationWithoutMetricsTests.java} (90%) rename spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/{actuate => }/observation/ObservationRestClientCustomizerTests.java (96%) rename spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/{actuate => }/observation/ObservationRestTemplateCustomizerTests.java (96%) rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfiguration.java => spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/WebClientObservationAutoConfiguration.java} (73%) create mode 100644 spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/package-info.java rename spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/{actuate => }/observation/ObservationWebClientCustomizer.java (96%) rename spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/{actuate => }/observation/package-info.java (91%) rename spring-boot-project/{spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfigurationTests.java => spring-boot-webclient/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationAutoConfigurationTests.java} (67%) rename spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/{actuate => }/observation/ObservationWebClientCustomizerTests.java (97%) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/HttpClientObservationsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/HttpClientObservationsAutoConfiguration.java deleted file mode 100644 index f6cc8acd5d..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/HttpClientObservationsAutoConfiguration.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012-2025 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.autoconfigure.observation.web.client; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.config.MeterFilter; -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationRegistry; - -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.metrics.OnlyOnceLoggingDenyMeterFilter; -import org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration; -import org.springframework.boot.metrics.autoconfigure.MetricsProperties; -import org.springframework.boot.metrics.autoconfigure.MetricsProperties.Web.Client; -import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; -import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.annotation.Order; - -/** - * {@link EnableAutoConfiguration Auto-configuration} for HTTP client-related - * observations. - * - * @author Jon Schneider - * @author Phillip Webb - * @author Stephane Nicoll - * @author Raheela Aslam - * @author Brian Clozel - * @author Moritz Halbritter - * @since 3.0.0 - */ -@AutoConfiguration(after = { ObservationAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class }, - afterName = { "org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration", - "org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration", - "org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration" }) -@ConditionalOnClass(Observation.class) -@ConditionalOnBean(ObservationRegistry.class) -@Import({ RestTemplateObservationConfiguration.class, WebClientObservationConfiguration.class, - RestClientObservationConfiguration.class }) -@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class }) -public class HttpClientObservationsAutoConfiguration { - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(MeterRegistry.class) - @ConditionalOnBean(MeterRegistry.class) - static class MeterFilterConfiguration { - - @Bean - @Order(0) - MeterFilter metricsHttpClientUriTagFilter(ObservationProperties observationProperties, - MetricsProperties metricsProperties) { - Client clientProperties = metricsProperties.getWeb().getClient(); - String name = observationProperties.getHttp().getClient().getRequests().getName(); - MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter( - () -> "Reached the maximum number of URI tags for '%s'. Are you using 'uriVariables'?" - .formatted(name)); - return MeterFilter.maximumAllowableTags(name, "uri", clientProperties.getMaxUriTags(), denyFilter); - } - - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 47bc5fc7a6..d68f316ac6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -16,7 +16,6 @@ org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAuto org.springframework.boot.actuate.autoconfigure.metrics.data.RepositoryMetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.observability.ObservabilityAutoConfiguration -org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration org.springframework.boot.actuate.autoconfigure.observation.web.reactive.WebFluxObservationAutoConfiguration org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration org.springframework.boot.actuate.autoconfigure.r2dbc.ConnectionFactoryHealthContributorAutoConfiguration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/test/MetricsIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/test/MetricsIntegrationTests.java index 0578161f0c..408397c110 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/test/MetricsIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/test/MetricsIntegrationTests.java @@ -35,7 +35,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.observability.ObservabilityAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.observation.web.reactive.WebFluxObservationAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; @@ -50,6 +49,7 @@ import org.springframework.boot.metrics.autoconfigure.system.SystemMetricsAutoCo import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration; +import org.springframework.boot.restclient.autoconfigure.observation.RestTemplateObservationAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration; @@ -147,7 +147,7 @@ class MetricsIntegrationTests { ObservabilityAutoConfiguration.class, JvmMetricsAutoConfiguration.class, LogbackMetricsAutoConfiguration.class, SystemMetricsAutoConfiguration.class, DataSourcePoolMetricsAutoConfiguration.class, HibernateMetricsAutoConfiguration.class, - HttpClientObservationsAutoConfiguration.class, WebFluxObservationAutoConfiguration.class, + RestTemplateObservationAutoConfiguration.class, WebFluxObservationAutoConfiguration.class, WebMvcObservationAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, RestTemplateAutoConfiguration.class, WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class, diff --git a/spring-boot-project/spring-boot-http-client/build.gradle b/spring-boot-project/spring-boot-http-client/build.gradle index fda85f22e6..01d2831b42 100644 --- a/spring-boot-project/spring-boot-http-client/build.gradle +++ b/spring-boot-project/spring-boot-http-client/build.gradle @@ -15,6 +15,7 @@ dependencies { implementation(project(":spring-boot-project:spring-boot-http-converter")) optional(project(":spring-boot-project:spring-boot-autoconfigure")) + optional(project(":spring-boot-project:spring-boot-metrics")) optional(project(":spring-boot-project:spring-boot-reactor-netty")) optional("org.apache.httpcomponents.client5:httpclient5") optional("org.apache.httpcomponents.core5:httpcore5-reactive") @@ -25,6 +26,7 @@ dependencies { testImplementation(project(":spring-boot-project:spring-boot-tomcat")) testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) testImplementation("org.springframework:spring-webflux") + testImplementation("io.micrometer:micrometer-observation-test") testRuntimeOnly("ch.qos.logback:logback-classic") } diff --git a/spring-boot-project/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfiguration.java new file mode 100644 index 0000000000..aed4500b60 --- /dev/null +++ b/spring-boot-project/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfiguration.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2025 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.http.client.autoconfigure.metrics; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.config.MeterFilter; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.metrics.OnlyOnceLoggingDenyMeterFilter; +import org.springframework.boot.metrics.autoconfigure.MetricsProperties; +import org.springframework.boot.metrics.autoconfigure.MetricsProperties.Web.Client; +import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.Order; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for HTTP client-related metrics. + * + * @author Jon Schneider + * @author Phillip Webb + * @author Stephane Nicoll + * @author Raheela Aslam + * @author Brian Clozel + * @author Moritz Halbritter + * @since 4.0.0 + */ +@AutoConfiguration(afterName = "org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration") +@ConditionalOnClass({ ObservationProperties.class, MeterRegistry.class, MetricsProperties.class }) +@ConditionalOnBean(MeterRegistry.class) +@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class }) +public class HttpClientMetricsAutoConfiguration { + + @Bean + @Order(0) + MeterFilter metricsHttpClientUriTagFilter(ObservationProperties observationProperties, + MetricsProperties metricsProperties) { + Client clientProperties = metricsProperties.getWeb().getClient(); + String name = observationProperties.getHttp().getClient().getRequests().getName(); + MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter( + () -> "Reached the maximum number of URI tags for '%s'. Are you using 'uriVariables'?".formatted(name)); + return MeterFilter.maximumAllowableTags(name, "uri", clientProperties.getMaxUriTags(), denyFilter); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/package-info.java b/spring-boot-project/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics/package-info.java similarity index 82% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/package-info.java rename to spring-boot-project/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics/package-info.java index b79f2f9ef1..7d8b003d1f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/package-info.java +++ b/spring-boot-project/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/metrics/package-info.java @@ -15,6 +15,6 @@ */ /** - * Auto-configuration for web client observation support. + * Auto-configuration for client-side HTTP metrics. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.http.client.autoconfigure.metrics; diff --git a/spring-boot-project/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index d62f740516..c67d7a2d6f 100644 --- a/spring-boot-project/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,3 @@ org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration +org.springframework.boot.http.client.autoconfigure.metrics.HttpClientMetricsAutoConfiguration org.springframework.boot.http.client.reactive.autoconfigure.ClientHttpConnectorAutoConfiguration diff --git a/spring-boot-project/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfigurationTests.java new file mode 100644 index 0000000000..7f3dd989fe --- /dev/null +++ b/spring-boot-project/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/metrics/HttpClientMetricsAutoConfigurationTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2025 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.http.client.autoconfigure.metrics; + +import java.time.Duration; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HttpClientMetricsAutoConfiguration}. + * + * @author Andy Wilkinson + */ +@ExtendWith(OutputCaptureExtension.class) +class HttpClientMetricsAutoConfigurationTests { + + @Test + void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) { + new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(HttpClientMetricsAutoConfiguration.class, MetricsAutoConfiguration.class)) + .withBean(SimpleMeterRegistry.class) + .withPropertyValues("management.metrics.web.client.max-uri-tags=2") + .run((context) -> { + MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); + for (int i = 0; i < 3; i++) { + meterRegistry.timer("http.client.requests", "uri", "/test/" + i).record(Duration.ofSeconds(1)); + } + assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(2); + assertThat(output).contains("Reached the maximum number of URI tags for 'http.client.requests'.") + .contains("Are you using 'uriVariables'?"); + }); + } + +} diff --git a/spring-boot-project/spring-boot-restclient/build.gradle b/spring-boot-project/spring-boot-restclient/build.gradle index 7eb8c86da2..02daa36981 100644 --- a/spring-boot-project/spring-boot-restclient/build.gradle +++ b/spring-boot-project/spring-boot-restclient/build.gradle @@ -14,10 +14,12 @@ dependencies { implementation(project(":spring-boot-project:spring-boot-http-converter")) optional(project(":spring-boot-project:spring-boot-autoconfigure")) + optional(project(":spring-boot-project:spring-boot-micrometer-observation")) optional("io.projectreactor.netty:reactor-netty-http") optional("org.apache.httpcomponents.client5:httpclient5") optional("org.eclipse.jetty:jetty-client") + testImplementation(project(":spring-boot-project:spring-boot-metrics")) testImplementation(project(":spring-boot-project:spring-boot-test")) testImplementation(project(":spring-boot-project:spring-boot-tomcat")) testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfiguration.java b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfiguration.java similarity index 77% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfiguration.java rename to spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfiguration.java index 763147e46c..f0de12c3ad 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfiguration.java +++ b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfiguration.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.restclient.autoconfigure.observation; import io.micrometer.observation.ObservationRegistry; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties; import org.springframework.boot.restclient.RestClientCustomizer; -import org.springframework.boot.restclient.actuate.observation.ObservationRestClientCustomizer; +import org.springframework.boot.restclient.observation.ObservationRestClientCustomizer; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.http.client.observation.ClientRequestObservationConvention; import org.springframework.http.client.observation.DefaultClientRequestObservationConvention; import org.springframework.web.client.RestClient; @@ -34,11 +35,14 @@ import org.springframework.web.client.RestClient; * Configure the instrumentation of {@link RestClient}. * * @author Moritz Halbritter + * @since 4.0.0 */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ RestClient.class, ObservationRestClientCustomizer.class }) -@ConditionalOnBean(RestClient.Builder.class) -class RestClientObservationConfiguration { +@AutoConfiguration +@ConditionalOnClass({ RestClient.class, ObservationRestClientCustomizer.class, ObservationRegistry.class, + ObservationProperties.class }) +@ConditionalOnBean({ RestClient.Builder.class, ObservationRegistry.class }) +@EnableConfigurationProperties(ObservationProperties.class) +public class RestClientObservationAutoConfiguration { @Bean RestClientCustomizer observationRestClientCustomizer(ObservationRegistry observationRegistry, diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfiguration.java b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfiguration.java similarity index 77% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfiguration.java rename to spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfiguration.java index 7013a6838a..998eb5ed15 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfiguration.java +++ b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfiguration.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.restclient.autoconfigure.observation; import io.micrometer.observation.ObservationRegistry; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties; import org.springframework.boot.restclient.RestTemplateBuilder; -import org.springframework.boot.restclient.actuate.observation.ObservationRestTemplateCustomizer; +import org.springframework.boot.restclient.observation.ObservationRestTemplateCustomizer; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.http.client.observation.ClientRequestObservationConvention; import org.springframework.http.client.observation.DefaultClientRequestObservationConvention; import org.springframework.web.client.RestTemplate; @@ -34,11 +35,14 @@ import org.springframework.web.client.RestTemplate; * Configure the instrumentation of {@link RestTemplate}. * * @author Brian Clozel + * @since 4.0.0 */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ RestTemplate.class, ObservationRestTemplateCustomizer.class }) -@ConditionalOnBean(RestTemplateBuilder.class) -class RestTemplateObservationConfiguration { +@AutoConfiguration +@ConditionalOnClass({ RestTemplate.class, ObservationRestTemplateCustomizer.class, ObservationRegistry.class, + ObservationProperties.class }) +@ConditionalOnBean({ ObservationRegistry.class, RestTemplateBuilder.class }) +@EnableConfigurationProperties(ObservationProperties.class) +public class RestTemplateObservationAutoConfiguration { @Bean ObservationRestTemplateCustomizer observationRestTemplateCustomizer(ObservationRegistry observationRegistry, diff --git a/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/package-info.java b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/package-info.java new file mode 100644 index 0000000000..f41e680c4b --- /dev/null +++ b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/observation/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2025 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. + */ + +/** + * Auto-configuration for RestClient and RestTemplate observation. + */ +package org.springframework.boot.restclient.autoconfigure.observation; diff --git a/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/ObservationRestClientCustomizer.java b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/ObservationRestClientCustomizer.java similarity index 97% rename from spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/ObservationRestClientCustomizer.java rename to spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/ObservationRestClientCustomizer.java index 49a562631a..8765c43e3e 100644 --- a/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/ObservationRestClientCustomizer.java +++ b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/ObservationRestClientCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.restclient.actuate.observation; +package org.springframework.boot.restclient.observation; import io.micrometer.observation.ObservationRegistry; diff --git a/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/ObservationRestTemplateCustomizer.java b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/ObservationRestTemplateCustomizer.java similarity index 96% rename from spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/ObservationRestTemplateCustomizer.java rename to spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/ObservationRestTemplateCustomizer.java index 5ede7226ef..9a4ef71b1a 100644 --- a/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/ObservationRestTemplateCustomizer.java +++ b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/ObservationRestTemplateCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.restclient.actuate.observation; +package org.springframework.boot.restclient.observation; import io.micrometer.observation.ObservationRegistry; diff --git a/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/package-info.java b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/package-info.java similarity index 91% rename from spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/package-info.java rename to spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/package-info.java index a6a549f230..c8bd317d6b 100644 --- a/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/actuate/observation/package-info.java +++ b/spring-boot-project/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/observation/package-info.java @@ -17,4 +17,4 @@ /** * Observation integration for RestClient and RestTemplate. */ -package org.springframework.boot.restclient.actuate.observation; +package org.springframework.boot.restclient.observation; diff --git a/spring-boot-project/spring-boot-restclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-restclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index be5538d040..8ad928185f 100644 --- a/spring-boot-project/spring-boot-restclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-restclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,5 @@ org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration -org.springframework.boot.restclient.service.autoconfigure.HttpServiceClientAutoConfiguration +org.springframework.boot.restclient.autoconfigure.observation.RestClientObservationAutoConfiguration +org.springframework.boot.restclient.autoconfigure.observation.RestTemplateObservationAutoConfiguration +org.springframework.boot.restclient.service.autoconfigure.HttpServiceClientAutoConfiguration \ No newline at end of file diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationTests.java b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationTests.java similarity index 74% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationTests.java rename to spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationTests.java index c6858f6331..445b131f67 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationTests.java +++ b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.restclient.autoconfigure.observation; import io.micrometer.common.KeyValues; import io.micrometer.core.instrument.MeterRegistry; @@ -26,15 +26,12 @@ import io.micrometer.observation.tck.TestObservationRegistry; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; -import org.springframework.boot.restclient.actuate.observation.ObservationRestClientCustomizer; import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; -import org.springframework.boot.restclient.test.MockServerRestClientCustomizer; +import org.springframework.boot.restclient.observation.ObservationRestClientCustomizer; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -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; @@ -50,18 +47,18 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers. import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; /** - * Tests for {@link RestClientObservationConfiguration}. + * Tests for {@link RestClientObservationAutoConfiguration}. * * @author Brian Clozel * @author Moritz Halbritter */ @ExtendWith(OutputCaptureExtension.class) -class RestClientObservationConfigurationTests { +class RestClientObservationAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(ObservationRegistry.class, TestObservationRegistry::create) .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, RestClientAutoConfiguration.class, - HttpClientObservationsAutoConfiguration.class)); + RestClientObservationAutoConfiguration.class)); @Test void contributesCustomizerBean() { @@ -102,35 +99,11 @@ class RestClientObservationConfigurationTests { }); } - @Test - void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) { - this.contextRunner.with(MetricsRun.simple()) - .withUserConfiguration(MetricsConfiguration.class) - .withPropertyValues("management.metrics.web.client.max-uri-tags=2") - .run((context) -> { - RestClientWithMockServer restClientWithMockServer = buildRestClientAndMockServer(context); - MockRestServiceServer server = restClientWithMockServer.mockServer(); - RestClient restClient = restClientWithMockServer.restClient(); - for (int i = 0; i < 3; i++) { - server.expect(requestTo("/test/" + i)).andRespond(withStatus(HttpStatus.OK)); - } - for (int i = 0; i < 3; i++) { - restClient.get().uri("/test/" + i, String.class).retrieve().toBodilessEntity(); - } - TestObservationRegistry registry = context.getBean(TestObservationRegistry.class); - assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3); - MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); - assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(2); - assertThat(output).contains("Reached the maximum number of URI tags for 'http.client.requests'.") - .contains("Are you using 'uriVariables'?"); - }); - } - @Test void backsOffWhenRestClientBuilderIsMissing() { - new ApplicationContextRunner().with(MetricsRun.simple()) + new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, - HttpClientObservationsAutoConfiguration.class)) + RestClientObservationAutoConfiguration.class)) .run((context) -> assertThat(context).doesNotHaveBean(ObservationRestClientCustomizer.class)); } @@ -144,9 +117,9 @@ class RestClientObservationConfigurationTests { private RestClientWithMockServer buildRestClientAndMockServer(AssertableApplicationContext context) { Builder builder = context.getBean(Builder.class); - MockServerRestClientCustomizer customizer = new MockServerRestClientCustomizer(); - customizer.customize(builder); - return new RestClientWithMockServer(builder.build(), customizer.getServer()); + MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build(); + server.expect(requestTo("/projects/spring-boot")).andRespond(withStatus(HttpStatus.OK)); + return new RestClientWithMockServer(builder.build(), server); } private record RestClientWithMockServer(RestClient restClient, MockRestServiceServer mockServer) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationWithoutMetricsTests.java b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationWithoutMetricsTests.java similarity index 82% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationWithoutMetricsTests.java rename to spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationWithoutMetricsTests.java index 96b988f820..872cc4cc7f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfigurationWithoutMetricsTests.java +++ b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestClientObservationAutoConfigurationWithoutMetricsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.restclient.autoconfigure.observation; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; @@ -24,12 +24,12 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; -import org.springframework.boot.restclient.test.MockServerRestClientCustomizer; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.http.HttpStatus; +import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient.Builder; @@ -38,7 +38,7 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers. import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; /** - * Tests for {@link RestClientObservationConfiguration} without Micrometer Metrics. + * Tests for {@link RestClientObservationAutoConfiguration} without Micrometer Metrics. * * @author Brian Clozel * @author Andy Wilkinson @@ -46,12 +46,12 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat */ @ExtendWith(OutputCaptureExtension.class) @ClassPathExclusions("micrometer-core-*.jar") -class RestClientObservationConfigurationWithoutMetricsTests { +class RestClientObservationAutoConfigurationWithoutMetricsTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(ObservationRegistry.class, TestObservationRegistry::create) .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, RestClientAutoConfiguration.class, - HttpClientObservationsAutoConfiguration.class)); + RestClientObservationAutoConfiguration.class)); @Test void restClientCreatedWithBuilderIsInstrumented() { @@ -65,9 +65,8 @@ class RestClientObservationConfigurationWithoutMetricsTests { private RestClient buildRestClient(AssertableApplicationContext context) { Builder builder = context.getBean(Builder.class); - MockServerRestClientCustomizer customizer = new MockServerRestClientCustomizer(); - customizer.customize(builder); - customizer.getServer().expect(requestTo("/projects/spring-boot")).andRespond(withStatus(HttpStatus.OK)); + MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build(); + server.expect(requestTo("/projects/spring-boot")).andRespond(withStatus(HttpStatus.OK)); return builder.build(); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationTests.java b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationTests.java similarity index 76% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationTests.java rename to spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationTests.java index 05fc2e32c7..e80571e04a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationTests.java +++ b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.restclient.autoconfigure.observation; import io.micrometer.common.KeyValues; import io.micrometer.core.instrument.MeterRegistry; @@ -26,15 +26,13 @@ import io.micrometer.observation.tck.TestObservationRegistry; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; import org.springframework.boot.restclient.RestTemplateBuilder; -import org.springframework.boot.restclient.actuate.observation.ObservationRestTemplateCustomizer; import org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration; +import org.springframework.boot.restclient.observation.ObservationRestTemplateCustomizer; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -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; @@ -49,17 +47,17 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers. import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; /** - * Tests for {@link RestTemplateObservationConfiguration}. + * Tests for {@link RestTemplateObservationAutoConfiguration}. * * @author Brian Clozel */ @ExtendWith(OutputCaptureExtension.class) -class RestTemplateObservationConfigurationTests { +class RestTemplateObservationAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(ObservationRegistry.class, TestObservationRegistry::create) .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, - RestTemplateAutoConfiguration.class, HttpClientObservationsAutoConfiguration.class)); + RestTemplateAutoConfiguration.class, RestTemplateObservationAutoConfiguration.class)); @Test void contributesCustomizerBean() { @@ -100,34 +98,11 @@ class RestTemplateObservationConfigurationTests { }); } - @Test - void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) { - this.contextRunner.with(MetricsRun.simple()) - .withUserConfiguration(MetricsConfiguration.class) - .withPropertyValues("management.metrics.web.client.max-uri-tags=2") - .run((context) -> { - RestTemplate restTemplate = context.getBean(RestTemplateBuilder.class).build(); - MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate); - for (int i = 0; i < 3; i++) { - server.expect(requestTo("/test/" + i)).andRespond(withStatus(HttpStatus.OK)); - } - for (int i = 0; i < 3; i++) { - restTemplate.getForObject("/test/" + i, String.class); - } - TestObservationRegistry registry = context.getBean(TestObservationRegistry.class); - assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3); - MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); - assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(2); - assertThat(output).contains("Reached the maximum number of URI tags for 'http.client.requests'.") - .contains("Are you using 'uriVariables'?"); - }); - } - @Test void backsOffWhenRestTemplateBuilderIsMissing() { - new ApplicationContextRunner().with(MetricsRun.simple()) + new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, - HttpClientObservationsAutoConfiguration.class)) + RestTemplateObservationAutoConfiguration.class)) .run((context) -> assertThat(context).doesNotHaveBean(ObservationRestTemplateCustomizer.class)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationWithoutMetricsTests.java b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationWithoutMetricsTests.java similarity index 90% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationWithoutMetricsTests.java rename to spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationWithoutMetricsTests.java index 3d743f9c53..1202bc0a34 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfigurationWithoutMetricsTests.java +++ b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/observation/RestTemplateObservationAutoConfigurationWithoutMetricsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.restclient.autoconfigure.observation; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; @@ -38,19 +38,19 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers. import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; /** - * Tests for {@link RestTemplateObservationConfiguration} without Micrometer Metrics. + * Tests for {@link RestTemplateObservationAutoConfiguration} without Micrometer Metrics. * * @author Brian Clozel * @author Andy Wilkinson */ @ExtendWith(OutputCaptureExtension.class) @ClassPathExclusions("micrometer-core-*.jar") -class RestTemplateObservationConfigurationWithoutMetricsTests { +class RestTemplateObservationAutoConfigurationWithoutMetricsTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(ObservationRegistry.class, TestObservationRegistry::create) .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, - RestTemplateAutoConfiguration.class, HttpClientObservationsAutoConfiguration.class)); + RestTemplateAutoConfiguration.class, RestTemplateObservationAutoConfiguration.class)); @Test void restTemplateCreatedWithBuilderIsInstrumented() { diff --git a/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/actuate/observation/ObservationRestClientCustomizerTests.java b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/observation/ObservationRestClientCustomizerTests.java similarity index 96% rename from spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/actuate/observation/ObservationRestClientCustomizerTests.java rename to spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/observation/ObservationRestClientCustomizerTests.java index 719de023f6..71045c80ed 100644 --- a/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/actuate/observation/ObservationRestClientCustomizerTests.java +++ b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/observation/ObservationRestClientCustomizerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.restclient.actuate.observation; +package org.springframework.boot.restclient.observation; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; diff --git a/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/actuate/observation/ObservationRestTemplateCustomizerTests.java b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/observation/ObservationRestTemplateCustomizerTests.java similarity index 96% rename from spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/actuate/observation/ObservationRestTemplateCustomizerTests.java rename to spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/observation/ObservationRestTemplateCustomizerTests.java index e9bae19008..2746d8ecd1 100644 --- a/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/actuate/observation/ObservationRestTemplateCustomizerTests.java +++ b/spring-boot-project/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/observation/ObservationRestTemplateCustomizerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.restclient.actuate.observation; +package org.springframework.boot.restclient.observation; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; diff --git a/spring-boot-project/spring-boot-webclient/build.gradle b/spring-boot-project/spring-boot-webclient/build.gradle index d39c15d34c..0de9e7cb87 100644 --- a/spring-boot-project/spring-boot-webclient/build.gradle +++ b/spring-boot-project/spring-boot-webclient/build.gradle @@ -15,6 +15,7 @@ dependencies { implementation(project(":spring-boot-project:spring-boot-http-codec")) optional(project(":spring-boot-project:spring-boot-autoconfigure")) + optional(project(":spring-boot-project:spring-boot-metrics")) optional(project(":spring-boot-project:spring-boot-reactor-netty")) optional("org.apache.httpcomponents.client5:httpclient5") optional("org.apache.httpcomponents.core5:httpcore5-reactive") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfiguration.java b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/WebClientObservationAutoConfiguration.java similarity index 73% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfiguration.java rename to spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/WebClientObservationAutoConfiguration.java index e4b3e42371..c59e0ecb3d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfiguration.java +++ b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/WebClientObservationAutoConfiguration.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.observation.web.client; +package org.springframework.boot.webclient.autoconfigure.observation; import io.micrometer.observation.ObservationRegistry; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.metrics.autoconfigure.MetricsProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties; -import org.springframework.boot.webclient.actuate.observation.ObservationWebClientCustomizer; +import org.springframework.boot.webclient.observation.ObservationWebClientCustomizer; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.client.ClientRequestObservationConvention; import org.springframework.web.reactive.function.client.DefaultClientRequestObservationConvention; import org.springframework.web.reactive.function.client.WebClient; @@ -33,15 +33,19 @@ import org.springframework.web.reactive.function.client.WebClient; * Configure the instrumentation of {@link WebClient}. * * @author Brian Clozel + * @since 4.0.0 */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ WebClient.class, ObservationWebClientCustomizer.class }) -class WebClientObservationConfiguration { +@AutoConfiguration( + beforeName = "org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration") +@ConditionalOnClass({ WebClient.class, ObservationWebClientCustomizer.class, ObservationRegistry.class, + ObservationProperties.class }) +@EnableConfigurationProperties(ObservationProperties.class) +public class WebClientObservationAutoConfiguration { @Bean ObservationWebClientCustomizer observationWebClientCustomizer(ObservationRegistry observationRegistry, ObjectProvider customConvention, - ObservationProperties observationProperties, MetricsProperties metricsProperties) { + ObservationProperties observationProperties) { String name = observationProperties.getHttp().getClient().getRequests().getName(); ClientRequestObservationConvention observationConvention = customConvention .getIfAvailable(() -> new DefaultClientRequestObservationConvention(name)); diff --git a/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/package-info.java b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/package-info.java new file mode 100644 index 0000000000..3c82d71ace --- /dev/null +++ b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/observation/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2025 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. + */ + +/** + * Auto-configuration for WebClient observation support. + */ +package org.springframework.boot.webclient.autoconfigure.observation; diff --git a/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/actuate/observation/ObservationWebClientCustomizer.java b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/observation/ObservationWebClientCustomizer.java similarity index 96% rename from spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/actuate/observation/ObservationWebClientCustomizer.java rename to spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/observation/ObservationWebClientCustomizer.java index 582b385415..3eac53c246 100644 --- a/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/actuate/observation/ObservationWebClientCustomizer.java +++ b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/observation/ObservationWebClientCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.webclient.actuate.observation; +package org.springframework.boot.webclient.observation; import io.micrometer.observation.ObservationRegistry; diff --git a/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/actuate/observation/package-info.java b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/observation/package-info.java similarity index 91% rename from spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/actuate/observation/package-info.java rename to spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/observation/package-info.java index 4c48bf40ce..ba50c779df 100644 --- a/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/actuate/observation/package-info.java +++ b/spring-boot-project/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/observation/package-info.java @@ -17,4 +17,4 @@ /** * Observation integration for WebClient. */ -package org.springframework.boot.webclient.actuate.observation; +package org.springframework.boot.webclient.observation; diff --git a/spring-boot-project/spring-boot-webclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-webclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 7566511fc8..e4564721d8 100644 --- a/spring-boot-project/spring-boot-webclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-webclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,3 @@ org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration +org.springframework.boot.webclient.autoconfigure.observation.WebClientObservationAutoConfiguration org.springframework.boot.webclient.service.autoconfigure.ReactiveHttpServiceClientAutoConfiguration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfigurationTests.java b/spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationAutoConfigurationTests.java similarity index 67% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfigurationTests.java rename to spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationAutoConfigurationTests.java index ccb0ece56b..27d266a663 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfigurationTests.java +++ b/spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationAutoConfigurationTests.java @@ -29,15 +29,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import reactor.core.publisher.Mono; -import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; -import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.boot.webclient.actuate.observation.ObservationWebClientCustomizer; import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration; +import org.springframework.boot.webclient.autoconfigure.observation.WebClientObservationAutoConfiguration; +import org.springframework.boot.webclient.observation.ObservationWebClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; @@ -53,18 +51,18 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** - * Tests for {@link WebClientObservationConfiguration} + * Tests for {@link WebClientObservationAutoConfiguration}. * * @author Brian Clozel * @author Stephane Nicoll */ @ExtendWith(OutputCaptureExtension.class) -class WebClientObservationConfigurationTests { +class WebClientObservationAutoConfigurationTests { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple()) + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(ObservationRegistry.class, TestObservationRegistry::create) - .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, WebClientAutoConfiguration.class, - HttpClientObservationsAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, WebClientAutoConfiguration.class)) + .withUserConfiguration(WebClientObservationAutoConfiguration.class); @Test void contributesCustomizerBean() { @@ -98,49 +96,6 @@ class WebClientObservationConfigurationTests { }); } - @Test - void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) { - this.contextRunner.withUserConfiguration(MetricsConfiguration.class) - .withPropertyValues("management.metrics.web.client.max-uri-tags=2") - .run((context) -> { - TestObservationRegistry registry = getInitializedRegistry(context); - assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3); - MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); - assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(1); - // MeterFilter.maximumAllowableTags() works with prefix matching. - assertThat(meterRegistry.find("http.client.requests.active").longTaskTimers()).hasSize(1); - assertThat(output).contains("Reached the maximum number of URI tags for 'http.client.requests'.") - .contains("Are you using 'uriVariables'?"); - }); - } - - @Test - void shouldNotDenyNorLogIfMaxUrisIsNotReached(CapturedOutput output) { - this.contextRunner.withUserConfiguration(MetricsConfiguration.class) - .withPropertyValues("management.metrics.web.client.max-uri-tags=5") - .run((context) -> { - TestObservationRegistry registry = getInitializedRegistry(context); - assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3); - MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); - assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(3); - assertThat(output).doesNotContain("Reached the maximum number of URI tags for 'http.client.requests'.") - .doesNotContain("Are you using 'uriVariables'?"); - }); - } - - private TestObservationRegistry getInitializedRegistry(AssertableApplicationContext context) { - WebClient webClient = mockWebClient(context.getBean(WebClient.Builder.class)); - TestObservationRegistry registry = context.getBean(TestObservationRegistry.class); - for (int i = 0; i < 3; i++) { - webClient.get() - .uri("https://example.org/projects/" + i) - .retrieve() - .toBodilessEntity() - .block(Duration.ofSeconds(30)); - } - return registry; - } - private void validateWebClient(WebClient.Builder builder, TestObservationRegistry registry) { WebClient webClient = mockWebClient(builder); assertThat(registry).doesNotHaveAnyObservation(); diff --git a/spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/actuate/observation/ObservationWebClientCustomizerTests.java b/spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/observation/ObservationWebClientCustomizerTests.java similarity index 97% rename from spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/actuate/observation/ObservationWebClientCustomizerTests.java rename to spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/observation/ObservationWebClientCustomizerTests.java index 63f7816926..9a7dc405bd 100644 --- a/spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/actuate/observation/ObservationWebClientCustomizerTests.java +++ b/spring-boot-project/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/observation/ObservationWebClientCustomizerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.webclient.actuate.observation; +package org.springframework.boot.webclient.observation; import io.micrometer.observation.tck.TestObservationRegistry; import org.junit.jupiter.api.Test;