diff --git a/spring-cloud-sleuth-dependencies/pom.xml b/spring-cloud-sleuth-dependencies/pom.xml
index 9832e1789..353f3c4d0 100644
--- a/spring-cloud-sleuth-dependencies/pom.xml
+++ b/spring-cloud-sleuth-dependencies/pom.xml
@@ -14,9 +14,9 @@
spring-cloud-sleuth-dependencies
Spring Cloud Sleuth Dependencies
- 2.2.0
- 1.1.1
- 2.1.1
+ 2.2.1
+ 1.1.2
+ 2.1.3
@@ -105,6 +105,30 @@
zipkin-reporter
${zipkin-reporter2.version}
+
+ io.zipkin.reporter2
+ zipkin-sender-kafka11
+ ${zipkin-reporter2.version}
+
+
+
+ org.apache.kafka
+ kafka-clients
+
+
+
+
+ io.zipkin.reporter2
+ zipkin-sender-amqp-client
+ ${zipkin-reporter2.version}
+
+
+
+ com.rabbitmq
+ amqp-client
+
+
+
diff --git a/spring-cloud-sleuth-samples/pom.xml b/spring-cloud-sleuth-samples/pom.xml
index bcb26e971..0ea917c50 100644
--- a/spring-cloud-sleuth-samples/pom.xml
+++ b/spring-cloud-sleuth-samples/pom.xml
@@ -59,17 +59,17 @@
io.zipkin.java
zipkin
- 2.2.0
+ 2.2.1
io.zipkin.zipkin2
zipkin
- 2.2.0
+ 2.2.1
io.zipkin.java
zipkin-server
- 2.2.0
+ 2.2.1
diff --git a/spring-cloud-sleuth-zipkin2/pom.xml b/spring-cloud-sleuth-zipkin2/pom.xml
index 370f2c357..9f8ea167b 100644
--- a/spring-cloud-sleuth-zipkin2/pom.xml
+++ b/spring-cloud-sleuth-zipkin2/pom.xml
@@ -68,6 +68,24 @@
io.zipkin.reporter2
zipkin-reporter
+
+ io.zipkin.reporter2
+ zipkin-sender-kafka11
+
+
+ org.springframework.kafka
+ spring-kafka
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-sender-amqp-client
+
+
+ org.springframework.amqp
+ spring-rabbit
+ true
+
org.springframework
spring-messaging
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java
index 1363481a2..027edb285 100644
--- a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java
@@ -16,25 +16,17 @@
package org.springframework.cloud.sleuth.zipkin2;
-import java.net.URI;
-import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
-
import java.util.concurrent.TimeUnit;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.cloud.client.ServiceInstance;
-import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.sleuth.Sampler;
@@ -44,13 +36,11 @@ import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
import org.springframework.cloud.sleuth.sampler.PercentageBasedSampler;
import org.springframework.cloud.sleuth.sampler.SamplerProperties;
+import org.springframework.cloud.sleuth.zipkin2.sender.ZipkinSenderConfigurationImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
-import org.springframework.http.HttpMethod;
-import org.springframework.web.client.RequestCallback;
-import org.springframework.web.client.ResponseExtractor;
-import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import zipkin2.Span;
import zipkin2.reporter.AsyncReporter;
@@ -77,10 +67,10 @@ import zipkin2.reporter.Sender;
@EnableConfigurationProperties({ZipkinProperties.class, SamplerProperties.class})
@ConditionalOnProperty(value = "spring.zipkin.enabled", matchIfMissing = true)
@AutoConfigureBefore(TraceAutoConfiguration.class)
+@Import(ZipkinSenderConfigurationImportSelector.class)
public class ZipkinAutoConfiguration {
@Autowired(required = false) List spanAdjusters = new ArrayList<>();
- @Autowired ZipkinUrlExtractor extractor;
/**
* Accepts a sender so you can plug-in any standard one. Returns a Reporter so you can also
@@ -100,55 +90,6 @@ public class ZipkinAutoConfiguration {
.build(zipkin.getEncoder());
}
- @Bean
- @ConditionalOnMissingBean
- public Sender restTemplateSender(ZipkinProperties zipkin,
- ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
- RestTemplate restTemplate = new ZipkinRestTemplateWrapper(zipkin, this.extractor);
- zipkinRestTemplateCustomizer.customize(restTemplate);
- return new RestTemplateSender(restTemplate, zipkin.getBaseUrl(), zipkin.getEncoder());
- }
-
- @Configuration
- @ConditionalOnClass(DiscoveryClient.class)
- static class DiscoveryClientZipkinUrlExtractorConfiguration {
-
- @Autowired(required = false) DiscoveryClient discoveryClient;
-
- @Bean
- ZipkinUrlExtractor zipkinUrlExtractor() {
- final DiscoveryClient discoveryClient = this.discoveryClient;
- return new ZipkinUrlExtractor() {
- @Override
- public URI zipkinUrl(ZipkinProperties zipkinProperties) {
- if (discoveryClient != null) {
- URI uri = URI.create(zipkinProperties.getBaseUrl());
- String host = uri.getHost();
- List instances = discoveryClient.getInstances(host);
- if (!instances.isEmpty()) {
- return instances.get(0).getUri();
- }
- }
- return URI.create(zipkinProperties.getBaseUrl());
- }
- };
- }
- }
-
- @Configuration
- @ConditionalOnMissingClass("org.springframework.cloud.client.discovery.DiscoveryClient")
- static class DefaultZipkinUrlExtractorConfiguration {
- @Bean
- ZipkinUrlExtractor zipkinUrlExtractor() {
- return new ZipkinUrlExtractor() {
- @Override
- public URI zipkinUrl(ZipkinProperties zipkinProperties) {
- return URI.create(zipkinProperties.getBaseUrl());
- }
- };
- }
- }
-
@Bean
@ConditionalOnMissingBean
public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(ZipkinProperties zipkinProperties) {
@@ -164,7 +105,7 @@ public class ZipkinAutoConfiguration {
@Bean
public SpanReporter zipkinSpanListener(Reporter reporter, EndpointLocator endpointLocator,
Environment environment) {
- return new ZipkinSpanListener(reporter, endpointLocator, environment, this.spanAdjusters);
+ return new ZipkinSpanReporter(reporter, endpointLocator, environment, this.spanAdjusters);
}
@Configuration
@@ -220,52 +161,3 @@ public class ZipkinAutoConfiguration {
}
}
}
-
-/**
- * Internal interface to provide a way to retrieve Zipkin URI. If there's no discovery client
- * then this value will be taken from the properties. Otherwise host will be assumed to
- * be a service id.
- */
-interface ZipkinUrlExtractor {
- URI zipkinUrl(ZipkinProperties zipkinProperties);
-}
-
-/**
- * Resolves at runtime where the Zipkin server is. If there's no discovery client then
- * {@link URI} from the properties is taken. Otherwise service discovery is pinged
- * for current Zipkin address.
- */
-class ZipkinRestTemplateWrapper extends RestTemplate {
-
- private static final Log log = LogFactory.getLog(ZipkinRestTemplateWrapper.class);
-
- private final ZipkinProperties zipkinProperties;
- private final ZipkinUrlExtractor extractor;
-
- ZipkinRestTemplateWrapper(ZipkinProperties zipkinProperties,
- ZipkinUrlExtractor extractor) {
- this.zipkinProperties = zipkinProperties;
- this.extractor = extractor;
- }
-
- @Override protected T doExecute(URI originalUrl, HttpMethod method,
- RequestCallback requestCallback,
- ResponseExtractor responseExtractor) throws RestClientException {
- URI uri = this.extractor.zipkinUrl(this.zipkinProperties);
- URI newUri = resolvedZipkinUri(originalUrl, uri);
- return super.doExecute(newUri, method, requestCallback, responseExtractor);
- }
-
- private URI resolvedZipkinUri(URI originalUrl, URI resolvedZipkinUri) {
- try {
- return new URI(resolvedZipkinUri.getScheme(), resolvedZipkinUri.getUserInfo(),
- resolvedZipkinUri.getHost(), resolvedZipkinUri.getPort(), originalUrl.getPath(),
- originalUrl.getQuery(), originalUrl.getFragment());
- } catch (URISyntaxException e) {
- if (log.isDebugEnabled()) {
- log.debug("Failed to create the new URI from original [" + originalUrl + "] and new one [" + resolvedZipkinUri + "]");
- }
- return originalUrl;
- }
- }
-}
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanListener.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporter.java
similarity index 96%
rename from spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanListener.java
rename to spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporter.java
index 333dc7f53..08df19355 100644
--- a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanListener.java
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporter.java
@@ -32,9 +32,9 @@ import zipkin2.reporter.Reporter;
/**
* Listener of Sleuth events. Reports to Zipkin via {@link Reporter}.
*/
-public class ZipkinSpanListener implements SpanReporter {
+public class ZipkinSpanReporter implements SpanReporter {
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
- .getLog(ZipkinSpanListener.class);
+ .getLog(ZipkinSpanReporter.class);
private final Reporter reporter;
private final Environment environment;
@@ -46,7 +46,7 @@ public class ZipkinSpanListener implements SpanReporter {
// Visible for testing
final EndpointLocator endpointLocator;
- public ZipkinSpanListener(Reporter reporter, EndpointLocator endpointLocator,
+ public ZipkinSpanReporter(Reporter reporter, EndpointLocator endpointLocator,
Environment environment, List spanAdjusters) {
this.reporter = reporter;
this.endpointLocator = endpointLocator;
@@ -202,4 +202,9 @@ public class ZipkinSpanListener implements SpanReporter {
}
}
}
+
+ @Override
+ public String toString(){
+ return "ZipkinSpanReporter(" + this.reporter + ")";
+ }
}
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/RestTemplateSender.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/RestTemplateSender.java
similarity index 98%
rename from spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/RestTemplateSender.java
rename to spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/RestTemplateSender.java
index e83bde0bb..969d74ee4 100644
--- a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/RestTemplateSender.java
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/RestTemplateSender.java
@@ -1,4 +1,4 @@
-package org.springframework.cloud.sleuth.zipkin2;
+package org.springframework.cloud.sleuth.zipkin2.sender;
import java.io.IOException;
import java.net.URI;
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinKafkaSenderConfiguration.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinKafkaSenderConfiguration.java
new file mode 100644
index 000000000..321e1522d
--- /dev/null
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinKafkaSenderConfiguration.java
@@ -0,0 +1,51 @@
+package org.springframework.cloud.sleuth.zipkin2.sender;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.kafka.common.serialization.ByteArraySerializer;
+import org.springframework.beans.factory.annotation.Value;
+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.autoconfigure.kafka.KafkaProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.kafka11.KafkaSender;
+
+@Configuration
+@ConditionalOnClass(ByteArraySerializer.class)
+@ConditionalOnBean(KafkaProperties.class)
+@ConditionalOnMissingBean(Sender.class)
+@Conditional(ZipkinSenderCondition.class)
+class ZipkinKafkaSenderConfiguration {
+ @Value("${spring.zipkin.kafka.topic:zipkin}")
+ private String topic;
+
+ @Bean Sender kafkaSender(KafkaProperties config) {
+ Map properties = config.buildProducerProperties();
+ properties.put("key.serializer", ByteArraySerializer.class.getName());
+ properties.put("value.serializer", ByteArraySerializer.class.getName());
+ // Kafka expects the input to be a String, but KafkaProperties returns a list
+ Object bootstrapServers = properties.get("bootstrap.servers");
+ if (bootstrapServers instanceof List) {
+ properties.put("bootstrap.servers", join((List) bootstrapServers));
+ }
+ return KafkaSender.newBuilder()
+ .topic(this.topic)
+ .overrides(properties)
+ .build();
+ }
+
+ static String join(List> parts) {
+ StringBuilder to = new StringBuilder();
+ for (int i = 0, length = parts.size(); i < length; i++) {
+ to.append(parts.get(i));
+ if (i + 1 < length) {
+ to.append(',');
+ }
+ }
+ return to.toString();
+ }
+}
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRabbitSenderConfiguration.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRabbitSenderConfiguration.java
new file mode 100644
index 000000000..fac0187a1
--- /dev/null
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRabbitSenderConfiguration.java
@@ -0,0 +1,29 @@
+package org.springframework.cloud.sleuth.zipkin2.sender;
+
+import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.amqp.RabbitMQSender;
+
+@Configuration
+@ConditionalOnBean(CachingConnectionFactory.class)
+@ConditionalOnMissingBean(Sender.class)
+@Conditional(ZipkinSenderCondition.class)
+class ZipkinRabbitSenderConfiguration {
+ @Value("${spring.zipkin.rabbitmq.queue:zipkin}")
+ private String queue;
+
+ @Bean Sender rabbitSender(CachingConnectionFactory connectionFactory, RabbitProperties config) {
+ return RabbitMQSender.newBuilder()
+ .connectionFactory(connectionFactory.getRabbitConnectionFactory())
+ .queue(this.queue)
+ .addresses(config.determineAddresses())
+ .build();
+ }
+}
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfiguration.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfiguration.java
new file mode 100644
index 000000000..60d299d5a
--- /dev/null
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfiguration.java
@@ -0,0 +1,131 @@
+package org.springframework.cloud.sleuth.zipkin2.sender;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
+import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.web.client.RequestCallback;
+import org.springframework.web.client.ResponseExtractor;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+import zipkin2.reporter.Sender;
+
+@Configuration
+@ConditionalOnMissingBean(Sender.class)
+@Conditional(ZipkinSenderCondition.class)
+class ZipkinRestTemplateSenderConfiguration {
+ @Autowired ZipkinUrlExtractor extractor;
+
+ @Bean
+ @ConditionalOnMissingBean
+ public Sender restTemplateSender(ZipkinProperties zipkin,
+ ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
+ RestTemplate restTemplate = new ZipkinRestTemplateWrapper(zipkin, this.extractor);
+ zipkinRestTemplateCustomizer.customize(restTemplate);
+ return new RestTemplateSender(restTemplate, zipkin.getBaseUrl(), zipkin.getEncoder());
+ }
+
+ @Configuration
+ @ConditionalOnMissingClass("org.springframework.cloud.client.discovery.DiscoveryClient")
+ static class DefaultZipkinUrlExtractorConfiguration {
+ @Bean
+ ZipkinUrlExtractor zipkinUrlExtractor() {
+ return new ZipkinUrlExtractor() {
+ @Override
+ public URI zipkinUrl(ZipkinProperties zipkinProperties) {
+ return URI.create(zipkinProperties.getBaseUrl());
+ }
+ };
+ }
+ }
+
+ @Configuration
+ @ConditionalOnClass(DiscoveryClient.class)
+ static class DiscoveryClientZipkinUrlExtractorConfiguration {
+
+ @Autowired(required = false) DiscoveryClient discoveryClient;
+
+ @Bean
+ ZipkinUrlExtractor zipkinUrlExtractor() {
+ final DiscoveryClient discoveryClient = this.discoveryClient;
+ return new ZipkinUrlExtractor() {
+ @Override
+ public URI zipkinUrl(ZipkinProperties zipkinProperties) {
+ if (discoveryClient != null) {
+ URI uri = URI.create(zipkinProperties.getBaseUrl());
+ String host = uri.getHost();
+ List instances = discoveryClient.getInstances(host);
+ if (!instances.isEmpty()) {
+ return instances.get(0).getUri();
+ }
+ }
+ return URI.create(zipkinProperties.getBaseUrl());
+ }
+ };
+ }
+ }
+}
+
+/**
+ * Resolves at runtime where the Zipkin server is. If there's no discovery client then {@link URI}
+ * from the properties is taken. Otherwise service discovery is pinged for current Zipkin address.
+ */
+class ZipkinRestTemplateWrapper extends RestTemplate {
+
+ private static final Log log = LogFactory.getLog(ZipkinRestTemplateWrapper.class);
+
+ private final ZipkinProperties zipkinProperties;
+ private final ZipkinUrlExtractor extractor;
+
+ ZipkinRestTemplateWrapper(ZipkinProperties zipkinProperties,
+ ZipkinUrlExtractor extractor) {
+ this.zipkinProperties = zipkinProperties;
+ this.extractor = extractor;
+ }
+
+ @Override protected T doExecute(URI originalUrl, HttpMethod method,
+ RequestCallback requestCallback,
+ ResponseExtractor responseExtractor) throws RestClientException {
+ URI uri = this.extractor.zipkinUrl(this.zipkinProperties);
+ URI newUri = resolvedZipkinUri(originalUrl, uri);
+ return super.doExecute(newUri, method, requestCallback, responseExtractor);
+ }
+
+ private URI resolvedZipkinUri(URI originalUrl, URI resolvedZipkinUri) {
+ try {
+ return new URI(resolvedZipkinUri.getScheme(), resolvedZipkinUri.getUserInfo(),
+ resolvedZipkinUri.getHost(), resolvedZipkinUri.getPort(), originalUrl.getPath(),
+ originalUrl.getQuery(), originalUrl.getFragment());
+ } catch (URISyntaxException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to create the new URI from original ["
+ + originalUrl
+ + "] and new one ["
+ + resolvedZipkinUri
+ + "]");
+ }
+ return originalUrl;
+ }
+ }
+}
+
+/**
+ * Internal interface to provide a way to retrieve Zipkin URI. If there's no discovery client then
+ * this value will be taken from the properties. Otherwise host will be assumed to be a service id.
+ */
+interface ZipkinUrlExtractor {
+ URI zipkinUrl(ZipkinProperties zipkinProperties);
+}
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinSenderCondition.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinSenderCondition.java
new file mode 100644
index 000000000..d7ac381fa
--- /dev/null
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinSenderCondition.java
@@ -0,0 +1,37 @@
+package org.springframework.cloud.sleuth.zipkin2.sender;
+
+import org.springframework.boot.autoconfigure.condition.ConditionMessage;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.boot.bind.RelaxedPropertyResolver;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.ClassMetadata;
+
+import static org.springframework.cloud.sleuth.zipkin2.sender.ZipkinSenderConfigurationImportSelector.getType;
+
+/** Attach this to any new sender configuration. */
+class ZipkinSenderCondition extends SpringBootCondition {
+
+ @Override
+ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata md) {
+ String sourceClass = "";
+ if (md instanceof ClassMetadata) {
+ sourceClass = ((ClassMetadata) md).getClassName();
+ }
+ ConditionMessage.Builder message = ConditionMessage.forCondition("ZipkinSender", sourceClass);
+ RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
+ context.getEnvironment(), "spring.zipkin.sender.");
+ if (!resolver.containsProperty("type")) {
+ return ConditionOutcome.match(message.because("automatic sender type"));
+ }
+
+ String senderType = getType(((AnnotationMetadata) md).getClassName());
+ String value = resolver.getProperty("type");
+ if (value.equals(senderType)) {
+ return ConditionOutcome.match(message.because(value + " sender type"));
+ }
+ return ConditionOutcome.noMatch(message.because(value + " sender type"));
+ }
+}
diff --git a/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinSenderConfigurationImportSelector.java b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinSenderConfigurationImportSelector.java
new file mode 100644
index 000000000..8fcb18842
--- /dev/null
+++ b/spring-cloud-sleuth-zipkin2/src/main/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinSenderConfigurationImportSelector.java
@@ -0,0 +1,36 @@
+package org.springframework.cloud.sleuth.zipkin2.sender;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.springframework.context.annotation.ImportSelector;
+import org.springframework.core.type.AnnotationMetadata;
+
+public class ZipkinSenderConfigurationImportSelector implements ImportSelector {
+
+ static final Map MAPPINGS;
+
+ // Classes below must be annotated with @Conditional(ZipkinSenderCondition.class)
+ static {
+ // Mappings in descending priority (highest is last)
+ Map mappings = new LinkedHashMap<>();
+ mappings.put("rabbit", ZipkinRabbitSenderConfiguration.class.getName());
+ mappings.put("kafka", ZipkinKafkaSenderConfiguration.class.getName());
+ mappings.put("web", ZipkinRestTemplateSenderConfiguration.class.getName());
+ MAPPINGS = Collections.unmodifiableMap(mappings);
+ }
+
+ @Override
+ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
+ return MAPPINGS.values().toArray(new String[0]);
+ }
+
+ static String getType(String configurationClassName) {
+ for (Map.Entry entry : MAPPINGS.entrySet()) {
+ if (entry.getValue().equals(configurationClassName)) {
+ return entry.getKey();
+ }
+ }
+ throw new IllegalStateException("Unknown configuration class " + configurationClassName);
+ }
+}
diff --git a/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java b/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java
index 45ebea279..7373621a5 100644
--- a/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java
+++ b/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java
@@ -23,13 +23,18 @@ import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
+import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanReporter;
import org.springframework.cloud.sleuth.metric.TraceMetricsAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import zipkin2.reporter.amqp.RabbitMQSender;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
@@ -92,4 +97,78 @@ public class ZipkinAutoConfigurationTests {
then(request.getPath()).isEqualTo("/api/v1/spans");
then(request.getBody().readUtf8()).contains("binaryAnnotations");
}
+
+ @Test
+ public void overrideRabbitMQQueue() throws Exception {
+ context = new AnnotationConfigApplicationContext();
+ addEnvironment(context, "spring.zipkin.rabbitmq.queue:zipkin2");
+ context.register(
+ PropertyPlaceholderAutoConfiguration.class,
+ TraceMetricsAutoConfiguration.class,
+ RabbitAutoConfiguration.class,
+ ZipkinAutoConfiguration.class);
+ context.refresh();
+
+ SpanReporter spanReporter = context.getBean(SpanReporter.class);
+ assertThat(spanReporter).extracting("reporter.sender.queue")
+ .contains("zipkin2");
+
+ context.close();
+ }
+
+ @Test
+ public void overrideKafkaTopic() throws Exception {
+ context = new AnnotationConfigApplicationContext();
+ addEnvironment(context, "spring.zipkin.kafka.topic:zipkin2");
+ context.register(
+ PropertyPlaceholderAutoConfiguration.class,
+ TraceMetricsAutoConfiguration.class,
+ KafkaAutoConfiguration.class,
+ ZipkinAutoConfiguration.class);
+ context.refresh();
+
+ SpanReporter spanReporter = context.getBean(SpanReporter.class);
+ assertThat(spanReporter).extracting("reporter.sender.topic")
+ .contains("zipkin2");
+
+ context.close();
+ }
+
+ @Test
+ public void canOverrideBySender() throws Exception {
+ context = new AnnotationConfigApplicationContext();
+ addEnvironment(context, "spring.zipkin.sender.type:web");
+ context.register(
+ PropertyPlaceholderAutoConfiguration.class,
+ TraceMetricsAutoConfiguration.class,
+ RabbitAutoConfiguration.class,
+ KafkaAutoConfiguration.class,
+ ZipkinAutoConfiguration.class);
+ context.refresh();
+
+ SpanReporter spanReporter = context.getBean(SpanReporter.class);
+ assertThat(spanReporter).extracting("reporter.sender").allSatisfy(
+ s -> assertThat(s.getClass().getSimpleName()).isEqualTo("RestTemplateSender")
+ );
+
+ context.close();
+ }
+
+ @Test
+ public void rabbitWinsWhenKafkaPresent() throws Exception {
+ context = new AnnotationConfigApplicationContext();
+ context.register(
+ PropertyPlaceholderAutoConfiguration.class,
+ TraceMetricsAutoConfiguration.class,
+ RabbitAutoConfiguration.class,
+ KafkaAutoConfiguration.class,
+ ZipkinAutoConfiguration.class);
+ context.refresh();
+
+ SpanReporter spanReporter = context.getBean(SpanReporter.class);
+ assertThat(spanReporter).extracting("reporter.sender")
+ .allSatisfy(s -> assertThat(s).isInstanceOf(RabbitMQSender.class));
+
+ context.close();
+ }
}
diff --git a/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java b/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java
index 5162d22e8..8dd988b9d 100644
--- a/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java
+++ b/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java
@@ -26,8 +26,10 @@ import org.springframework.test.context.junit4.SpringRunner;
import zipkin.junit.ZipkinRule;
@RunWith(SpringRunner.class)
-@SpringBootTest(classes = ZipkinDiscoveryClientTests.Config.class,
- properties = "spring.zipkin.baseUrl=http://zipkin/")
+@SpringBootTest(classes = ZipkinDiscoveryClientTests.Config.class, properties = {
+ "spring.zipkin.baseUrl=http://zipkin/",
+ "spring.zipkin.sender.type=web" // override default priority which picks rabbit due to classpath
+})
public class ZipkinDiscoveryClientTests {
@ClassRule public static ZipkinRule ZIPKIN_RULE = new ZipkinRule();
diff --git a/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanListenerTests.java b/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporterTests.java
similarity index 87%
rename from spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanListenerTests.java
rename to spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporterTests.java
index ec409647f..eb7c07235 100644
--- a/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanListenerTests.java
+++ b/spring-cloud-sleuth-zipkin2/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporterTests.java
@@ -32,7 +32,7 @@ import org.springframework.cloud.sleuth.SpanAdjuster;
import org.springframework.cloud.sleuth.SpanReporter;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
-import org.springframework.cloud.sleuth.zipkin2.ZipkinSpanListenerTests.TestConfiguration;
+import org.springframework.cloud.sleuth.zipkin2.ZipkinSpanReporterTests.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@@ -52,12 +52,12 @@ import static org.junit.Assert.assertEquals;
*/
@SpringBootTest(classes = TestConfiguration.class)
@RunWith(SpringRunner.class)
-public class ZipkinSpanListenerTests {
+public class ZipkinSpanReporterTests {
@Autowired Tracer tracer;
@Autowired TestConfiguration test;
- @Autowired ZipkinSpanListener spanListener;
- @Autowired Reporter spanReporter;
+ @Autowired ZipkinSpanReporter spanReporter;
+ @Autowired Reporter zipkinReporter;
@Autowired MockEnvironment mockEnvironment;
@Autowired EndpointLocator endpointLocator;
@@ -76,7 +76,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("hystrix/retry"); // System.currentTimeMillis
span.stop();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.timestamp())
.isEqualTo(span.getBegin() * 1000);
@@ -97,7 +97,7 @@ public class ZipkinSpanListenerTests {
Thread.sleep(20);
span.stop();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.timestamp()).isEqualTo(span.getBegin() * 1000);
long clientSendTimestamp = span.logs().stream()
@@ -114,7 +114,7 @@ public class ZipkinSpanListenerTests {
@Test
public void doesntSetDurationWhenStillRunning() {
Span span = Span.builder().traceId(1L).name("http:api").build();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.timestamp())
.isGreaterThan(0); // sanity check it did start
@@ -128,16 +128,16 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent("hystrix/retry");
this.parent.tag("spring-boot/version", "1.3.1.RELEASE");
- zipkin2.Span result = this.spanListener.convert(this.parent);
+ zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.localEndpoint())
- .isEqualTo(this.spanListener.endpointLocator.local());
+ .isEqualTo(this.spanReporter.endpointLocator.local());
}
/** zipkin's Endpoint.serviceName should never be null. */
@Test
public void localEndpointIncludesServiceName() {
- assertThat(this.spanListener.endpointLocator.local().serviceName())
+ assertThat(this.spanReporter.endpointLocator.local().serviceName())
.isNotEmpty();
}
@@ -155,7 +155,7 @@ public class ZipkinSpanListenerTests {
Span context = this.tracer.createSpan("http:child", this.parent);
context.logEvent(Span.CLIENT_SEND);
logServerReceived(this.parent);
- logServerSent(this.spanListener, this.parent);
+ logServerSent(this.spanReporter, this.parent);
this.tracer.close(context);
assertEquals(2, this.test.zipkinSpans.size());
}
@@ -178,7 +178,7 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent("hystrix/retry");
this.parent.stop();
- zipkin2.Span result = this.spanListener.convert(this.parent);
+ zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.tags())
.isEmpty();
@@ -190,7 +190,7 @@ public class ZipkinSpanListenerTests {
this.parent.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "fooservice");
this.parent.stop();
- zipkin2.Span result = this.spanListener.convert(this.parent);
+ zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.remoteEndpoint())
.isEqualTo(Endpoint.newBuilder().serviceName("fooservice").build());
@@ -201,7 +201,7 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent("cs");
this.parent.stop();
- zipkin2.Span result = this.spanListener.convert(this.parent);
+ zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.remoteEndpoint())
.isNull();
@@ -211,7 +211,7 @@ public class ZipkinSpanListenerTests {
public void converts128BitTraceId() {
Span span = Span.builder().traceIdHigh(1L).traceId(2L).spanId(3L).name("foo").build();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.traceId())
.isEqualTo("00000000000000010000000000000002");
@@ -223,7 +223,7 @@ public class ZipkinSpanListenerTests {
this.parent.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "fooservice");
this.parent.stop();
- zipkin2.Span result = this.spanListener.convert(this.parent);
+ zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.remoteEndpoint())
.isEqualTo(Endpoint.newBuilder().serviceName("fooservice").build());
@@ -233,7 +233,7 @@ public class ZipkinSpanListenerTests {
public void shouldNotReportToZipkinWhenSpanIsNotExportable() {
Span span = Span.builder().exportable(false).build();
- this.spanListener.report(span);
+ this.spanReporter.report(span);
assertThat(this.test.zipkinSpans).isEmpty();
}
@@ -243,7 +243,7 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent(Span.CLIENT_SEND);
this.mockEnvironment.setProperty("vcap.application.instance_id", "foo");
- zipkin2.Span result = this.spanListener.convert(this.parent);
+ zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.tags())
.containsExactly(entry(Span.INSTANCEID, "foo"));
@@ -252,7 +252,7 @@ public class ZipkinSpanListenerTests {
@Test
public void shouldNotAddAnyServiceIdTagWhenSpanContainsRpcEventAndThereIsNoEnvironment() {
this.parent.logEvent(Span.CLIENT_RECV);
- ZipkinSpanListener spanListener = new ZipkinSpanListener(this.spanReporter,
+ ZipkinSpanReporter spanListener = new ZipkinSpanReporter(this.zipkinReporter,
this.endpointLocator, null, new ArrayList<>());
zipkin2.Span result = spanListener.convert(this.parent);
@@ -264,7 +264,7 @@ public class ZipkinSpanListenerTests {
@Test
public void should_adjust_span_before_reporting_it() {
this.parent.logEvent(Span.CLIENT_RECV);
- ZipkinSpanListener spanListener = new ZipkinSpanListener(this.spanReporter,
+ ZipkinSpanReporter spanListener = new ZipkinSpanReporter(this.zipkinReporter,
this.endpointLocator, null, Collections.singletonList(
span -> Span.builder().from(span).name("foo").build())) {
@Override String defaultInstanceId() {
@@ -288,7 +288,7 @@ public class ZipkinSpanListenerTests {
.build();
span.stop();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.duration()).isNotNull();
assertThat(result.timestamp()).isNotNull();
@@ -302,7 +302,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("ss");
span.stop();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.SERVER);
then(result.annotations()).isEmpty();
@@ -315,7 +315,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("cr");
span.stop();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.CLIENT);
then(result.annotations()).isEmpty();
@@ -327,7 +327,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("ms");
span.stop();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.PRODUCER);
then(result.annotations()).isEmpty();
@@ -339,7 +339,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("mr");
span.stop();
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.CONSUMER);
then(result.annotations()).isEmpty();
@@ -364,7 +364,7 @@ public class ZipkinSpanListenerTests {
}
// end::service_name[]
- zipkin2.Span result = this.spanListener.convert(span);
+ zipkin2.Span result = this.spanReporter.convert(span);
then(result.remoteEndpoint())
.isEqualTo(Endpoint.newBuilder().serviceName("redis").ip("1.2.3.4").port(1234).build());