Separate Zipkin tracing auto-configuration
This commit is contained in:
@@ -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.
|
||||
* <p>
|
||||
* 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<ZipkinHttpClientBuilderCustomizer> customizers,
|
||||
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
|
||||
ObjectProvider<HttpEndpointSupplier.Factory> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<ZipkinHttpClientBuilderCustomizer> customizers,
|
||||
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
|
||||
ObjectProvider<HttpEndpointSupplier.Factory> 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)
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user