Break cycles between Zipkin senders and HTTP client observation
Previously, RestTemplateBuilder and WebClient.Builder beans were used to create the HTTP client for sending out spans. Those same beans are also instrumented for observability which results in a cycle. This commit breaks the cycle by not using the application-web builders to create the RestTemplate and WebClient's used by the Zipkin senders. Instead, builders are created inline, with new callbacks being introduced to allow the user to customize these Zipkin-specific builders. See gh-32528
This commit is contained in:
parent
13c638ba39
commit
b41ed44b60
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.tracing.zipkin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
|
||||
import zipkin2.Span;
|
||||
import zipkin2.codec.BytesEncoder;
|
||||
|
|
@ -73,12 +75,12 @@ class ZipkinConfigurations {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Sender.class)
|
||||
@ConditionalOnBean(RestTemplateBuilder.class)
|
||||
ZipkinRestTemplateSender restTemplateSender(ZipkinProperties properties,
|
||||
RestTemplateBuilder restTemplateBuilder) {
|
||||
RestTemplate restTemplate = restTemplateBuilder.setConnectTimeout(properties.getConnectTimeout())
|
||||
.setReadTimeout(properties.getReadTimeout()).build();
|
||||
return new ZipkinRestTemplateSender(properties.getEndpoint(), restTemplate);
|
||||
List<ZipkinRestTemplateBuilderCustomizer> customizers) {
|
||||
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder()
|
||||
.setConnectTimeout(properties.getConnectTimeout()).setReadTimeout(properties.getReadTimeout());
|
||||
customizers.forEach((c) -> c.customize(restTemplateBuilder));
|
||||
return new ZipkinRestTemplateSender(properties.getEndpoint(), restTemplateBuilder.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -90,10 +92,11 @@ class ZipkinConfigurations {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Sender.class)
|
||||
@ConditionalOnBean(WebClient.Builder.class)
|
||||
ZipkinWebClientSender webClientSender(ZipkinProperties properties, WebClient.Builder webClientBuilder) {
|
||||
WebClient webClient = webClientBuilder.build();
|
||||
return new ZipkinWebClientSender(properties.getEndpoint(), webClient);
|
||||
ZipkinWebClientSender webClientSender(ZipkinProperties properties,
|
||||
List<ZipkinWebClientBuilderCustomizer> customizers) {
|
||||
WebClient.Builder builder = WebClient.builder();
|
||||
customizers.forEach((c) -> c.customize(builder));
|
||||
return new ZipkinWebClientSender(properties.getEndpoint(), builder.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.actuate.autoconfigure.tracing.zipkin;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import zipkin2.reporter.Sender;
|
||||
import zipkin2.reporter.urlconnection.URLConnectionSender;
|
||||
|
||||
|
|
@ -26,12 +27,12 @@ import org.springframework.boot.test.context.FilteredClassLoader;
|
|||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
|
|
@ -66,6 +67,8 @@ class ZipkinConfigurationsSenderConfigurationTests {
|
|||
assertThat(context).doesNotHaveBean(URLConnectionSender.class);
|
||||
assertThat(context).hasSingleBean(Sender.class);
|
||||
assertThat(context).hasSingleBean(ZipkinWebClientSender.class);
|
||||
then(context.getBean(ZipkinWebClientBuilderCustomizer.class)).should()
|
||||
.customize(ArgumentMatchers.any());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -90,9 +93,9 @@ class ZipkinConfigurationsSenderConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void willUseRestTemplateInNonWebApplicationIfUrlConnectionSenderIsNotAvailable() {
|
||||
void willUseRestTemplateInNonWebApplicationIfUrlConnectionSenderAndWebclientAreNotAvailable() {
|
||||
this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader("zipkin2.reporter.urlconnection")).run((context) -> {
|
||||
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(URLConnectionSender.class);
|
||||
assertThat(context).hasSingleBean(Sender.class);
|
||||
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
|
||||
|
|
@ -100,9 +103,9 @@ class ZipkinConfigurationsSenderConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void willUseRestTemplateInServletWebApplicationIfUrlConnectionSenderIsNotAvailable() {
|
||||
void willUseRestTemplateInServletWebApplicationIfUrlConnectionSenderAndWebClientNotAvailable() {
|
||||
this.servletContextRunner.withUserConfiguration(RestTemplateConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader("zipkin2.reporter.urlconnection")).run((context) -> {
|
||||
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(URLConnectionSender.class);
|
||||
assertThat(context).hasSingleBean(Sender.class);
|
||||
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
|
||||
|
|
@ -110,9 +113,9 @@ class ZipkinConfigurationsSenderConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void willUseRestTemplateInReactiveWebApplicationIfUrlConnectionSenderIsNotAvailable() {
|
||||
void willUseRestTemplateInReactiveWebApplicationIfUrlConnectionSenderAndWebClientAreNotAvailable() {
|
||||
this.reactiveContextRunner.withUserConfiguration(RestTemplateConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader("zipkin2.reporter.urlconnection")).run((context) -> {
|
||||
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(URLConnectionSender.class);
|
||||
assertThat(context).hasSingleBean(Sender.class);
|
||||
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
|
||||
|
|
@ -140,8 +143,8 @@ class ZipkinConfigurationsSenderConfigurationTests {
|
|||
private static class RestTemplateConfiguration {
|
||||
|
||||
@Bean
|
||||
RestTemplateBuilder restTemplateBuilder() {
|
||||
return new RestTemplateBuilder();
|
||||
ZipkinRestTemplateBuilderCustomizer restTemplateBuilder() {
|
||||
return mock(ZipkinRestTemplateBuilderCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -150,8 +153,8 @@ class ZipkinConfigurationsSenderConfigurationTests {
|
|||
private static class WebClientConfiguration {
|
||||
|
||||
@Bean
|
||||
WebClient.Builder webClientBuilder() {
|
||||
return WebClient.builder();
|
||||
ZipkinWebClientBuilderCustomizer webClientBuilder() {
|
||||
return mock(ZipkinWebClientBuilderCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue