diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java index a6725d799..89f198355 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java @@ -19,9 +19,7 @@ package org.springframework.cloud.sleuth.zipkin2; import java.util.concurrent.TimeUnit; import zipkin2.Span; -import zipkin2.codec.BytesEncoder; import zipkin2.reporter.AsyncReporter; -import zipkin2.reporter.InMemoryReporterMetrics; import zipkin2.reporter.Reporter; import zipkin2.reporter.ReporterMetrics; import zipkin2.reporter.Sender; @@ -86,18 +84,11 @@ public class ZipkinAutoConfiguration { @Bean(REPORTER_BEAN_NAME) @ConditionalOnMissingBean(name = REPORTER_BEAN_NAME) public Reporter reporter(ReporterMetrics reporterMetrics, - ZipkinProperties zipkin, @Qualifier(SENDER_BEAN_NAME) Sender sender, - BytesEncoder spanBytesEncoder) { + ZipkinProperties zipkin, @Qualifier(SENDER_BEAN_NAME) Sender sender) { // historical constraint. Note: AsyncReporter supports memory bounds return AsyncReporter.builder(sender).queuedMaxSpans(1000) .messageTimeout(zipkin.getMessageTimeout(), TimeUnit.SECONDS) - .metrics(reporterMetrics).build(spanBytesEncoder); - } - - @Bean - @ConditionalOnMissingBean - public BytesEncoder spanBytesEncoder(ZipkinProperties zipkinProperties) { - return zipkinProperties.getEncoder(); + .metrics(reporterMetrics).build(zipkin.getEncoder()); } @Bean @@ -107,18 +98,6 @@ public class ZipkinAutoConfiguration { return new DefaultZipkinRestTemplateCustomizer(zipkinProperties); } - /** - * Deprecated because this is moved to {@link TraceAutoConfiguration}. Left for - * backwards compatibility reasons. - * @deprecated - */ - @Bean - @ConditionalOnMissingBean - @Deprecated - ReporterMetrics zipkinReporterMetrics() { - return new InMemoryReporterMetrics(); - } - @Configuration @ConditionalOnMissingBean(EndpointLocator.class) @ConditionalOnProperty(value = "spring.zipkin.locator.discovery.enabled", havingValue = "false", matchIfMissing = true) diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfiguration.java new file mode 100644 index 000000000..391e20122 --- /dev/null +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfiguration.java @@ -0,0 +1,149 @@ +/* + * Copyright 2013-2019 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 + * + * http://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.cloud.sleuth.zipkin2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import zipkin2.Span; +import zipkin2.codec.BytesEncoder; +import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.InMemoryReporterMetrics; +import zipkin2.reporter.Reporter; +import zipkin2.reporter.ReporterMetrics; +import zipkin2.reporter.Sender; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ConfigurationCondition; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} that will provide backwards compatibility to be able to support + * multiple tracing systems on the classpath. + * + * Needs to be auto-configured before {@link ZipkinAutoConfiguration} in order to create a + * {@link Reporter span reporter} if needed. + * + * @author Tim Ysewyn + * @since 2.1.0 + * @see ZipkinAutoConfiguration + * @deprecated + */ +@Configuration +@ConditionalOnProperty(value = { "spring.sleuth.enabled", + "spring.zipkin.enabled" }, matchIfMissing = true) +@AutoConfigureAfter({ ZipkinAutoConfiguration.class }) +@Deprecated +public class ZipkinBackwardsCompatibilityAutoConfiguration { + + /** + * Reporter that is depending on a {@link Sender} bean which is created in another + * auto-configuration than {@link ZipkinAutoConfiguration}. + */ + @Bean + @Conditional(BackwardsCompatibilityCondition.class) + @Deprecated + public Reporter reporter(ReporterMetrics reporterMetrics, + ZipkinProperties zipkin, BytesEncoder spanBytesEncoder, + DefaultListableBeanFactory beanFactory) { + List beanNames = new ArrayList<>( + Arrays.asList(beanFactory.getBeanNamesForType(Sender.class))); + beanNames.remove(ZipkinAutoConfiguration.SENDER_BEAN_NAME); + Sender sender = (Sender) beanFactory.getBean(beanNames.get(0)); + // historical constraint. Note: AsyncReporter supports memory bounds + return AsyncReporter.builder(sender).queuedMaxSpans(1000) + .messageTimeout(zipkin.getMessageTimeout(), TimeUnit.SECONDS) + .metrics(reporterMetrics).build(spanBytesEncoder); + } + + /** + * Only used for creating a reporter bean with the method above + * @deprecated + */ + @Bean + @ConditionalOnMissingBean + @Deprecated + public BytesEncoder spanBytesEncoder(ZipkinProperties zipkinProperties) { + return zipkinProperties.getEncoder(); + } + + /** + * Deprecated because this is moved to {@link TraceAutoConfiguration}. Left for + * backwards compatibility reasons. + * @deprecated + */ + @Bean + @ConditionalOnMissingBean + @Deprecated + ReporterMetrics zipkinReporterMetrics() { + return new InMemoryReporterMetrics(); + } + + static class BackwardsCompatibilityCondition extends SpringBootCondition + implements ConfigurationCondition { + + @Override + public ConfigurationPhase getConfigurationPhase() { + return ConfigurationPhase.REGISTER_BEAN; + } + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + Assert.isInstanceOf(DefaultListableBeanFactory.class, + context.getBeanFactory()); + DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) context + .getBeanFactory(); + int foundSenders = listableBeanFactory + .getBeanNamesForType(Sender.class).length; + + // Previously we supported 1 Sender bean at a time + // which could be overridden by another auto-configuration. + // Now we support both the overridden bean and our default zipkinSender bean. + if (foundSenders < 2) { + return ConditionOutcome.noMatch( + "We don't support backwards compatibility for more than 2 Sender beans"); + } + int foundReporters = listableBeanFactory + .getBeanNamesForType(Reporter.class).length; + // Check if we need to provide a Reporter bean for the overridden Sender bean + if (foundReporters == foundSenders) { + return ConditionOutcome.noMatch( + "Both tracing systems already define their own Reporter bean"); + } + return ConditionOutcome.match(); + } + + } + +} diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinKafkaSenderConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinKafkaSenderConfiguration.java index a9932e026..ad7b9717e 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinKafkaSenderConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinKafkaSenderConfiguration.java @@ -35,7 +35,7 @@ import zipkin2.reporter.kafka11.KafkaSender; @Configuration @ConditionalOnClass(ByteArraySerializer.class) @ConditionalOnBean(KafkaProperties.class) -@ConditionalOnMissingBean(Sender.class) +@ConditionalOnMissingBean(name = ZipkinAutoConfiguration.SENDER_BEAN_NAME) @Conditional(ZipkinSenderCondition.class) @ConditionalOnProperty(value = "spring.zipkin.sender.type", havingValue = "kafka") class ZipkinKafkaSenderConfiguration { diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRabbitSenderConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRabbitSenderConfiguration.java index 0213eb205..2b7da99ab 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRabbitSenderConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRabbitSenderConfiguration.java @@ -30,7 +30,7 @@ import zipkin2.reporter.amqp.RabbitMQSender; @Configuration @ConditionalOnBean(CachingConnectionFactory.class) -@ConditionalOnMissingBean(Sender.class) +@ConditionalOnMissingBean(name = ZipkinAutoConfiguration.SENDER_BEAN_NAME) @Conditional(ZipkinSenderCondition.class) class ZipkinRabbitSenderConfiguration { diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfiguration.java index ea5235db6..6c9619459 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfiguration.java @@ -43,7 +43,7 @@ import org.springframework.web.client.RestTemplate; import zipkin2.reporter.Sender; @Configuration -@ConditionalOnMissingBean(Sender.class) +@ConditionalOnMissingBean(name = ZipkinAutoConfiguration.SENDER_BEAN_NAME) @Conditional(ZipkinSenderCondition.class) @EnableConfigurationProperties(ZipkinSenderProperties.class) class ZipkinRestTemplateSenderConfiguration { @@ -52,7 +52,6 @@ class ZipkinRestTemplateSenderConfiguration { ZipkinUrlExtractor extractor; @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME) - @ConditionalOnMissingBean(name = ZipkinAutoConfiguration.SENDER_BEAN_NAME) public Sender restTemplateSender(ZipkinProperties zipkin, ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) { RestTemplate restTemplate = new ZipkinRestTemplateWrapper(zipkin, this.extractor); diff --git a/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories index 7a0e23d33..d9fa3ceb7 100644 --- a/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ # Auto Configuration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration \ No newline at end of file +org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration,\ + org.springframework.cloud.sleuth.zipkin2.ZipkinBackwardsCompatibilityAutoConfiguration \ No newline at end of file diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java index 0aba38b50..a7f01e116 100644 --- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java @@ -79,7 +79,7 @@ public class ZipkinAutoConfigurationTests { this.server.url("/").toString()); this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, - Config.class); + Config.class, ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); Span span = this.context.getBean(Tracing.class).tracer().nextSpan().name("foo") .tag("foo", "bar").start(); @@ -106,7 +106,7 @@ public class ZipkinAutoConfigurationTests { environment().setProperty("spring.zipkin.encoder", "JSON_V1"); this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, - Config.class); + Config.class, ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); Span span = this.context.getBean(Tracing.class).tracer().nextSpan().name("foo") .tag("foo", "bar").start(); @@ -124,9 +124,11 @@ public class ZipkinAutoConfigurationTests { public void overrideRabbitMQQueue() throws Exception { this.context = new AnnotationConfigApplicationContext(); environment().setProperty("spring.zipkin.rabbitmq.queue", "zipkin2"); + environment().setProperty("spring.zipkin.sender.type", "rabbit"); this.context.register(PropertyPlaceholderAutoConfiguration.class, RabbitAutoConfiguration.class, ZipkinAutoConfiguration.class, - TraceAutoConfiguration.class); + TraceAutoConfiguration.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); then(this.context.getBean(Sender.class)).isInstanceOf(RabbitMQSender.class); @@ -141,7 +143,8 @@ public class ZipkinAutoConfigurationTests { environment().setProperty("spring.zipkin.sender.type", "kafka"); this.context.register(PropertyPlaceholderAutoConfiguration.class, KafkaAutoConfiguration.class, ZipkinAutoConfiguration.class, - TraceAutoConfiguration.class); + TraceAutoConfiguration.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); then(this.context.getBean(Sender.class)).isInstanceOf(KafkaSender.class); @@ -155,7 +158,8 @@ public class ZipkinAutoConfigurationTests { environment().setProperty("spring.zipkin.sender.type", "web"); this.context.register(PropertyPlaceholderAutoConfiguration.class, RabbitAutoConfiguration.class, KafkaAutoConfiguration.class, - ZipkinAutoConfiguration.class, TraceAutoConfiguration.class); + ZipkinAutoConfiguration.class, TraceAutoConfiguration.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); then(this.context.getBean(Sender.class).getClass().getName()) @@ -170,7 +174,8 @@ public class ZipkinAutoConfigurationTests { environment().setProperty("spring.zipkin.sender.type", "WEB"); this.context.register(PropertyPlaceholderAutoConfiguration.class, RabbitAutoConfiguration.class, KafkaAutoConfiguration.class, - ZipkinAutoConfiguration.class, TraceAutoConfiguration.class); + ZipkinAutoConfiguration.class, TraceAutoConfiguration.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); then(this.context.getBean(Sender.class).getClass().getName()) @@ -184,7 +189,8 @@ public class ZipkinAutoConfigurationTests { this.context = new AnnotationConfigApplicationContext(); this.context.register(PropertyPlaceholderAutoConfiguration.class, RabbitAutoConfiguration.class, KafkaAutoConfiguration.class, - ZipkinAutoConfiguration.class, TraceAutoConfiguration.class); + ZipkinAutoConfiguration.class, TraceAutoConfiguration.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); then(this.context.getBean(Sender.class)).isInstanceOf(RabbitMQSender.class); @@ -199,7 +205,8 @@ public class ZipkinAutoConfigurationTests { this.server.url("/").toString()); this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, - Config.class, MultipleReportersConfig.class); + Config.class, MultipleReportersConfig.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); then(this.context.getBeansOfType(Sender.class)).hasSize(2); @@ -226,12 +233,33 @@ public class ZipkinAutoConfigurationTests { Awaitility.await().untilAsserted(() -> then(sender.isSpanSent()).isTrue()); } + @Test + public void supportsMultipleReportersWithBackwardsCompatibilty() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + environment().setProperty("spring.zipkin.base-url", + this.server.url("/").toString()); + this.context.register(ZipkinAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, + Config.class, BackwardsCompatibilityConfig.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); + this.context.refresh(); + + then(this.context.getBeansOfType(Sender.class)).hasSize(2); + then(this.context.getBeansOfType(Sender.class)) + .containsKeys(ZipkinAutoConfiguration.SENDER_BEAN_NAME, "rabbitSender"); + + then(this.context.getBeansOfType(Reporter.class)).hasSize(2); + then(this.context.getBeansOfType(Reporter.class)) + .containsKeys(ZipkinAutoConfiguration.REPORTER_BEAN_NAME, "reporter"); + } + @Test public void shouldOverrideDefaultBeans() { this.context = new AnnotationConfigApplicationContext(); this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, - Config.class, MyConfig.class); + Config.class, MyConfig.class, + ZipkinBackwardsCompatibilityAutoConfiguration.class); this.context.refresh(); then(this.context.getBeansOfType(Sender.class)).hasSize(1); @@ -337,6 +365,16 @@ public class ZipkinAutoConfigurationTests { } + @Configuration + protected static class BackwardsCompatibilityConfig { + + @Bean + Sender rabbitSender() { + return RabbitMQSender.create("localhost"); + } + + } + // tag::override_default_beans[] @Configuration