Move zipkin.Span types in the OpenTelemetry auto-configuration

Brave can work without zipkin2 on the classpath, OpenTelemetry can't.
To not force users to have zipkin2 on the classpath, move it into the
OpenTelemetry auto-configuration.

See gh-39049
This commit is contained in:
Moritz Halbritter 2024-02-21 10:06:30 +01:00
parent 5e844dbbdd
commit ed4d13a8bf
4 changed files with 57 additions and 32 deletions

View File

@ -16,10 +16,7 @@
package org.springframework.boot.actuate.autoconfigure.tracing.zipkin;
import zipkin2.Span;
import zipkin2.reporter.BytesEncoder;
import zipkin2.reporter.Encoding;
import zipkin2.reporter.SpanBytesEncoder;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.BraveConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.OpenTelemetryConfiguration;
@ -63,10 +60,4 @@ public class ZipkinAutoConfiguration {
};
}
@Bean
@ConditionalOnMissingBean(value = Span.class, parameterizedContainer = BytesEncoder.class)
BytesEncoder<Span> zipkinSpanEncoder(Encoding encoding) {
return SpanBytesEncoder.forEncoding(encoding);
}
}

View File

@ -29,6 +29,7 @@ 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 zipkin2.reporter.urlconnection.URLConnectionSender;
@ -177,7 +178,7 @@ class ZipkinConfigurations {
@Bean
@ConditionalOnMissingBean(value = MutableSpan.class, parameterizedContainer = BytesEncoder.class)
BytesEncoder<MutableSpan> braveSpanEncoder(Encoding encoding,
BytesEncoder<MutableSpan> mutableSpanBytesEncoder(Encoding encoding,
ObjectProvider<Tag<Throwable>> throwableTagProvider) {
Tag<Throwable> throwableTag = throwableTagProvider.getIfAvailable(() -> Tags.ERROR);
return MutableSpanBytesEncoder.create(encoding, throwableTag);
@ -188,22 +189,28 @@ class ZipkinConfigurations {
@ConditionalOnBean(BytesMessageSender.class)
@ConditionalOnEnabledTracing
AsyncZipkinSpanHandler asyncZipkinSpanHandler(BytesMessageSender sender,
BytesEncoder<MutableSpan> braveSpanEncoder) {
return AsyncZipkinSpanHandler.newBuilder(sender).build(braveSpanEncoder);
BytesEncoder<MutableSpan> mutableSpanBytesEncoder) {
return AsyncZipkinSpanHandler.newBuilder(sender).build(mutableSpanBytesEncoder);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ZipkinSpanExporter.class)
@ConditionalOnClass({ ZipkinSpanExporter.class, Span.class })
static class OpenTelemetryConfiguration {
@Bean
@ConditionalOnMissingBean(value = Span.class, parameterizedContainer = BytesEncoder.class)
BytesEncoder<Span> spanBytesEncoder(Encoding encoding) {
return SpanBytesEncoder.forEncoding(encoding);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(BytesMessageSender.class)
@ConditionalOnEnabledTracing
ZipkinSpanExporter zipkinSpanExporter(BytesMessageSender sender, BytesEncoder<Span> zipkinSpanEncoder) {
return ZipkinSpanExporter.builder().setSender(sender).setEncoder(zipkinSpanEncoder).build();
ZipkinSpanExporter zipkinSpanExporter(BytesMessageSender sender, BytesEncoder<Span> spanBytesEncoder) {
return ZipkinSpanExporter.builder().setSender(sender).setEncoder(spanBytesEncoder).build();
}
}

View File

@ -40,8 +40,7 @@ class ZipkinAutoConfigurationTests {
@Test
void shouldSupplyBeans() {
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(Encoding.class)
.hasSingleBean(PropertiesZipkinConnectionDetails.class)
.hasBean("zipkinSpanEncoder"));
.hasSingleBean(PropertiesZipkinConnectionDetails.class));
}
@Test
@ -65,14 +64,8 @@ class ZipkinAutoConfigurationTests {
@Test
void shouldUseCustomConnectionDetailsWhenDefined() {
this.contextRunner.withBean(ZipkinConnectionDetails.class, () -> new ZipkinConnectionDetails() {
@Override
public String getSpanEndpoint() {
return "http://localhost";
}
})
this.contextRunner
.withBean(ZipkinConnectionDetails.class, () -> new FixedZipkinConnectionDetails("http://localhost"))
.run((context) -> assertThat(context).hasSingleBean(ZipkinConnectionDetails.class)
.doesNotHaveBean(PropertiesZipkinConnectionDetails.class));
}
@ -85,6 +78,21 @@ class ZipkinAutoConfigurationTests {
.run((context) -> assertThat(context).hasNotFailed());
}
private static final class FixedZipkinConnectionDetails implements ZipkinConnectionDetails {
private final String spanEndpoint;
private FixedZipkinConnectionDetails(String spanEndpoint) {
this.spanEndpoint = spanEndpoint;
}
@Override
public String getSpanEndpoint() {
return this.spanEndpoint;
}
}
@Configuration(proxyBeanMethods = false)
private static final class CustomConfiguration {

View File

@ -31,7 +31,6 @@ 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 OpenTelemetryConfiguration}.
@ -48,24 +47,42 @@ class ZipkinConfigurationsOpenTelemetryConfigurationTests {
@Test
void shouldSupplyBeans() {
this.contextRunner.withUserConfiguration(SenderConfiguration.class)
.withBean(BytesEncoder.class, () -> mock(BytesEncoder.class))
.run((context) -> assertThat(context).hasSingleBean(ZipkinSpanExporter.class));
this.contextRunner.withUserConfiguration(SenderConfiguration.class, CustomEncoderConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
assertThat(context).hasBean("customSpanEncoder");
});
}
@Test
void shouldNotSupplyZipkinSpanExporterIfSenderIsMissing() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ZipkinSpanExporter.class));
this.contextRunner.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinSpanExporter.class);
assertThat(context).hasBean("spanBytesEncoder");
});
}
@Test
void shouldNotSupplyZipkinSpanExporterIfNotOnClasspath() {
this.contextRunner.withClassLoader(new FilteredClassLoader("io.opentelemetry.exporter.zipkin"))
.withUserConfiguration(SenderConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(ZipkinSpanExporter.class));
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinSpanExporter.class);
assertThat(context).doesNotHaveBean("spanBytesEncoder");
});
}
@Test
void shouldBackOffIfZipkinIsNotOnClasspath() {
this.contextRunner.withClassLoader(new FilteredClassLoader("zipkin2.Span"))
.withUserConfiguration(SenderConfiguration.class)
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinSpanExporter.class);
assertThat(context).doesNotHaveBean("spanBytesEncoder");
});
}
@Test
void shouldBackOffOnCustomBeans() {
this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> {
@ -85,6 +102,7 @@ class ZipkinConfigurationsOpenTelemetryConfigurationTests {
this.contextRunner.withUserConfiguration(SenderConfiguration.class, CustomEncoderConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
assertThat(context).hasBean("customSpanEncoder");
assertThat(context.getBean(ZipkinSpanExporter.class)).extracting("encoder")
.isInstanceOf(CustomSpanEncoder.class)
.extracting("encoding")
@ -99,6 +117,7 @@ class ZipkinConfigurationsOpenTelemetryConfigurationTests {
CustomEncoderConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
assertThat(context).hasBean("customSpanEncoder");
assertThat(context.getBean(ZipkinSpanExporter.class)).extracting("encoder")
.isInstanceOf(CustomSpanEncoder.class)
.extracting("encoding")
@ -140,7 +159,7 @@ class ZipkinConfigurationsOpenTelemetryConfigurationTests {
private static final class CustomEncoderConfiguration {
@Bean
BytesEncoder<Span> encoder(Encoding encoding) {
BytesEncoder<Span> customSpanEncoder(Encoding encoding) {
return new CustomSpanEncoder(encoding);
}