diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java index 86fad15145..08139faeca 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java @@ -16,31 +16,31 @@ package org.springframework.boot.actuate.autoconfigure.tracing.zipkin; -import zipkin2.reporter.Encoding; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Builder; -import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.BraveConfiguration; -import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.OpenTelemetryConfiguration; -import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.SenderConfiguration; -import org.springframework.boot.autoconfigure.AutoConfiguration; +import zipkin2.reporter.BytesMessageSender; +import zipkin2.reporter.Encoding; +import zipkin2.reporter.HttpEndpointSupplier; +import zipkin2.reporter.HttpEndpointSuppliers; + +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; /** * {@link EnableAutoConfiguration Auto-configuration} for Zipkin. - *

- * It uses imports on {@link ZipkinConfigurations} to guarantee the correct configuration - * ordering. * * @author Moritz Halbritter + * @author Moritz Halbritter + * @author Stefan Bratanov + * @author Wick Dynex * @since 3.0.0 */ -@AutoConfiguration(afterName = "org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration") @ConditionalOnClass(Encoding.class) -@Import({ SenderConfiguration.class, BraveConfiguration.class, OpenTelemetryConfiguration.class }) @EnableConfigurationProperties(ZipkinProperties.class) public class ZipkinAutoConfiguration { @@ -59,4 +59,21 @@ public class ZipkinAutoConfiguration { }; } + @Bean + @ConditionalOnMissingBean(BytesMessageSender.class) + @ConditionalOnClass(HttpClient.class) + ZipkinHttpClientSender httpClientSender(ZipkinProperties properties, Encoding encoding, + ObjectProvider customizers, + ObjectProvider connectionDetailsProvider, + ObjectProvider endpointSupplierFactoryProvider) { + ZipkinConnectionDetails connectionDetails = connectionDetailsProvider + .getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties)); + HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider + .getIfAvailable(HttpEndpointSuppliers::constantFactory); + Builder httpClientBuilder = HttpClient.newBuilder().connectTimeout(properties.getConnectTimeout()); + customizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder)); + return new ZipkinHttpClientSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(), + httpClientBuilder.build(), properties.getReadTimeout()); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinTracingAutoConfiguration.java similarity index 65% rename from spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurations.java rename to spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinTracingAutoConfiguration.java index 9a95365b6f..b29d7a7aaf 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinTracingAutoConfiguration.java @@ -16,9 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.tracing.zipkin; -import java.net.http.HttpClient; -import java.net.http.HttpClient.Builder; - import brave.Tag; import brave.Tags; import brave.handler.MutableSpan; @@ -27,59 +24,36 @@ import zipkin2.Span; import zipkin2.reporter.BytesEncoder; import zipkin2.reporter.BytesMessageSender; import zipkin2.reporter.Encoding; -import zipkin2.reporter.HttpEndpointSupplier; -import zipkin2.reporter.HttpEndpointSuppliers; import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.brave.AsyncZipkinSpanHandler; import zipkin2.reporter.brave.MutableSpanBytesEncoder; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing; +import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinTracingAutoConfiguration.BraveConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinTracingAutoConfiguration.OpenTelemetryConfiguration; +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.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** - * Configurations for Zipkin. Those are imported by {@link ZipkinAutoConfiguration}. + * {@link EnableAutoConfiguration Auto-configuration} for Zipkin tracing. * * @author Moritz Halbritter * @author Stefan Bratanov * @author Wick Dynex + * @author Phillip Webb + * @since 4.0.0 */ -class ZipkinConfigurations { - - @Configuration(proxyBeanMethods = false) - @Import({ HttpClientSenderConfiguration.class }) - static class SenderConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(HttpClient.class) - @EnableConfigurationProperties(ZipkinProperties.class) - static class HttpClientSenderConfiguration { - - @Bean - @ConditionalOnMissingBean(BytesMessageSender.class) - ZipkinHttpClientSender httpClientSender(ZipkinProperties properties, Encoding encoding, - ObjectProvider customizers, - ObjectProvider connectionDetailsProvider, - ObjectProvider endpointSupplierFactoryProvider) { - ZipkinConnectionDetails connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties)); - HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider - .getIfAvailable(HttpEndpointSuppliers::constantFactory); - Builder httpClientBuilder = HttpClient.newBuilder().connectTimeout(properties.getConnectTimeout()); - customizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder)); - return new ZipkinHttpClientSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(), - httpClientBuilder.build(), properties.getReadTimeout()); - } - - } +@ConditionalOnClass(Encoding.class) +@AutoConfiguration(afterName = "org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration") +@Import({ BraveConfiguration.class, OpenTelemetryConfiguration.class }) +public class ZipkinTracingAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(AsyncZipkinSpanHandler.class) 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 d6412bd9f5..ebb414c4c9 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 @@ -63,6 +63,7 @@ org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoC org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingAutoConfiguration org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusExemplarsAutoConfiguration org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration +org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinTracingAutoConfiguration org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesAutoConfiguration org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesEndpointAutoConfiguration org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration @@ -71,4 +72,4 @@ org.springframework.boot.actuate.autoconfigure.web.server.jetty.JettyServletMana org.springframework.boot.actuate.autoconfigure.web.server.netty.NettyReactiveManagementContextAutoConfiguration org.springframework.boot.actuate.autoconfigure.web.server.undertow.UndertowReactiveManagementContextAutoConfiguration org.springframework.boot.actuate.autoconfigure.web.server.undertow.UndertowServletManagementContextAutoConfiguration -org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration +org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration \ 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/tracing/zipkin/ZipkinAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationTests.java index b568900acc..87877c8634 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationTests.java @@ -16,8 +16,12 @@ package org.springframework.boot.actuate.autoconfigure.tracing.zipkin; +import java.net.http.HttpClient; + import org.junit.jupiter.api.Test; +import zipkin2.reporter.BytesMessageSender; import zipkin2.reporter.Encoding; +import zipkin2.reporter.HttpEndpointSupplier; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; @@ -26,11 +30,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link ZipkinAutoConfiguration}. * * @author Moritz Halbritter + * @author Wick Dynex */ class ZipkinAutoConfigurationTests { @@ -39,8 +45,12 @@ class ZipkinAutoConfigurationTests { @Test void shouldSupplyBeans() { - this.contextRunner.run((context) -> assertThat(context).hasSingleBean(Encoding.class) - .hasSingleBean(PropertiesZipkinConnectionDetails.class)); + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(Encoding.class); + assertThat(context).hasSingleBean(PropertiesZipkinConnectionDetails.class); + assertThat(context).hasSingleBean(BytesMessageSender.class); + assertThat(context).hasSingleBean(ZipkinHttpClientSender.class); + }); } @Test @@ -50,13 +60,37 @@ class ZipkinAutoConfigurationTests { } @Test - void shouldBackOffOnCustomBeans() { - this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { + void shouldNotProvideHttpClientSenderIfHttpClientIsNotAvailable() { + this.contextRunner.withClassLoader(new FilteredClassLoader(HttpClient.class)) + .run((context) -> assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class)); + } + + @Test + void shouldBackOffOnCustomEncodingBeans() { + this.contextRunner.withUserConfiguration(CustomEncodingConfiguration.class).run((context) -> { assertThat(context).hasBean("customEncoding"); assertThat(context).hasSingleBean(Encoding.class); }); } + @Test + void shouldBackOffOnCustomSenderBeans() { + this.contextRunner.withUserConfiguration(CustomSenderConfiguration.class).run((context) -> { + assertThat(context).hasBean("customSender"); + assertThat(context).hasSingleBean(BytesMessageSender.class); + }); + } + + @Test + void shouldUseCustomHttpEndpointSupplierFactory() { + this.contextRunner.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class) + .run((context) -> { + ZipkinHttpClientSender httpClientSender = context.getBean(ZipkinHttpClientSender.class); + assertThat(httpClientSender).extracting("endpointSupplier") + .isInstanceOf(CustomHttpEndpointSupplier.class); + }); + } + @Test void definesPropertiesBasedConnectionDetailsByDefault() { this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesZipkinConnectionDetails.class)); @@ -94,7 +128,7 @@ class ZipkinAutoConfigurationTests { } @Configuration(proxyBeanMethods = false) - private static final class CustomConfiguration { + private static final class CustomEncodingConfiguration { @Bean Encoding customEncoding() { @@ -103,4 +137,52 @@ class ZipkinAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class CustomSenderConfiguration { + + @Bean + BytesMessageSender customSender() { + return mock(BytesMessageSender.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class CustomHttpEndpointSupplierFactoryConfiguration { + + @Bean + HttpEndpointSupplier.Factory httpEndpointSupplier() { + return new CustomHttpEndpointSupplierFactory(); + } + + } + + static class CustomHttpEndpointSupplierFactory implements HttpEndpointSupplier.Factory { + + @Override + public HttpEndpointSupplier create(String endpoint) { + return new CustomHttpEndpointSupplier(endpoint); + } + + } + + static class CustomHttpEndpointSupplier implements HttpEndpointSupplier { + + private final String endpoint; + + CustomHttpEndpointSupplier(String endpoint) { + this.endpoint = endpoint; + } + + @Override + public String get() { + return this.endpoint; + } + + @Override + public void close() { + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsBraveConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsBraveConfigurationTests.java index 25e1fd5a5f..4c71f91c77 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsBraveConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsBraveConfigurationTests.java @@ -28,7 +28,7 @@ import zipkin2.reporter.BytesMessageSender; import zipkin2.reporter.Encoding; import zipkin2.reporter.brave.AsyncZipkinSpanHandler; -import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.BraveConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinTracingAutoConfiguration.BraveConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsOpenTelemetryConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsOpenTelemetryConfigurationTests.java index 64db02d75d..186684b7b2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsOpenTelemetryConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsOpenTelemetryConfigurationTests.java @@ -23,7 +23,7 @@ import zipkin2.reporter.BytesEncoder; import zipkin2.reporter.BytesMessageSender; import zipkin2.reporter.Encoding; -import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.OpenTelemetryConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinTracingAutoConfiguration.OpenTelemetryConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsSenderConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsSenderConfigurationTests.java deleted file mode 100644 index 4767ecc209..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure-all/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurationsSenderConfigurationTests.java +++ /dev/null @@ -1,128 +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.tracing.zipkin; - -import java.net.http.HttpClient; - -import org.junit.jupiter.api.Test; -import zipkin2.reporter.BytesMessageSender; -import zipkin2.reporter.HttpEndpointSupplier; - -import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.HttpClientSenderConfiguration; -import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.SenderConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.FilteredClassLoader; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link SenderConfiguration}. - * - * @author Moritz Halbritter - * @author Wick Dynex - */ -class ZipkinConfigurationsSenderConfigurationTests { - - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(DefaultEncodingConfiguration.class, SenderConfiguration.class)); - - @Test - void shouldSupplyDefaultHttpClientSenderBean() { - this.contextRunner.run((context) -> { - assertThat(context).hasSingleBean(BytesMessageSender.class); - assertThat(context).hasSingleBean(ZipkinHttpClientSender.class); - }); - } - - @Test - void shouldNotProvideHttpClientSenderIfHttpClientIsNotAvailable() { - this.contextRunner.withUserConfiguration(HttpClientSenderConfiguration.class) - .withClassLoader(new FilteredClassLoader(HttpClient.class)) - .run((context) -> assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class)); - } - - @Test - void shouldBackOffOnCustomBeans() { - this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { - assertThat(context).hasBean("customSender"); - assertThat(context).hasSingleBean(BytesMessageSender.class); - }); - } - - @Test - void shouldUseCustomHttpEndpointSupplierFactory() { - this.contextRunner.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class) - .run((context) -> { - ZipkinHttpClientSender httpClientSender = context.getBean(ZipkinHttpClientSender.class); - assertThat(httpClientSender).extracting("endpointSupplier") - .isInstanceOf(CustomHttpEndpointSupplier.class); - }); - } - - @Configuration(proxyBeanMethods = false) - static class CustomConfiguration { - - @Bean - BytesMessageSender customSender() { - return mock(BytesMessageSender.class); - } - - } - - @Configuration(proxyBeanMethods = false) - static class CustomHttpEndpointSupplierFactoryConfiguration { - - @Bean - HttpEndpointSupplier.Factory httpEndpointSupplier() { - return new CustomHttpEndpointSupplierFactory(); - } - - } - - static class CustomHttpEndpointSupplierFactory implements HttpEndpointSupplier.Factory { - - @Override - public HttpEndpointSupplier create(String endpoint) { - return new CustomHttpEndpointSupplier(endpoint); - } - - } - - static class CustomHttpEndpointSupplier implements HttpEndpointSupplier { - - private final String endpoint; - - CustomHttpEndpointSupplier(String endpoint) { - this.endpoint = endpoint; - } - - @Override - public String get() { - return this.endpoint; - } - - @Override - public void close() { - } - - } - -}