Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/src/main/asciidoc/intro.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,18 @@ dependencies {
<2> Add the dependency to `spring-cloud-starter-zipkin`. That way, all nested dependencies get downloaded.
<3> To automatically configure RabbitMQ, add the `spring-rabbit` dependency.

=== Overriding the auto-configuration of Zipkin

Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0.
In order to get this to work, every tracing system needs to have a `Reporter<Span>` and `Sender`.
If you want to override the provided beans you need to give them a specific name.
To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`.

[source,java]
----
include::../../../../spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java[tags=override_default_beans,indent=0]
----

== Additional Resources

You can watch a video of https://twitter.com/reshmi9k[Reshmi Krishna] and https://twitter.com/mgrzejszczak[Marcin Grzejszczak] talking about Spring Cloud
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
Expand Down Expand Up @@ -32,7 +32,9 @@
import brave.sampler.Sampler;
import org.springframework.util.StringUtils;
import zipkin2.Span;
import zipkin2.reporter.InMemoryReporterMetrics;
import zipkin2.reporter.Reporter;
import zipkin2.reporter.ReporterMetrics;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -51,6 +53,7 @@
*
* @author Spencer Gibb
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 2.0.0
*/
@Configuration
Expand All @@ -68,6 +71,9 @@ public class TraceAutoConfiguration {
*/
public static final String DEFAULT_SERVICE_NAME = "default";

@Autowired(required = false)
List<Reporter<zipkin2.Span>> spanReporters = new ArrayList<>();

@Autowired(required = false)
List<SpanAdjuster> spanAdjusters = new ArrayList<>();

Expand All @@ -86,13 +92,12 @@ public class TraceAutoConfiguration {
Tracing tracing(
@Value("${spring.zipkin.service.name:${spring.application.name:default}}") String serviceName,
Propagation.Factory factory, CurrentTraceContext currentTraceContext,
Reporter<zipkin2.Span> reporter, Sampler sampler, ErrorParser errorParser,
SleuthProperties sleuthProperties) {
Sampler sampler, ErrorParser errorParser, SleuthProperties sleuthProperties) {
Tracing.Builder builder = Tracing.newBuilder().sampler(sampler)
.errorParser(errorParser)
.localServiceName(StringUtils.isEmpty(serviceName) ? DEFAULT_SERVICE_NAME : serviceName)
.propagationFactory(factory).currentTraceContext(currentTraceContext)
.spanReporter(adjustedReporter(reporter))
.spanReporter(compositeReporter())
.traceId128Bit(sleuthProperties.isTraceId128())
.supportsJoin(sleuthProperties.isSupportsJoin());
for (FinishedSpanHandler finishedSpanHandlerFactory : this.finishedSpanHandlers) {
Expand All @@ -101,13 +106,15 @@ Tracing tracing(
return builder.build();
}

private Reporter<zipkin2.Span> adjustedReporter(Reporter<zipkin2.Span> delegate) {
private Reporter<zipkin2.Span> compositeReporter() {
return (span) -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional: I would pull this into a static or package private class as it isn't debug friendly. a toString would be nice, and it would also be nice to not have to loop for the common case where there is only one reporter. Another optional would be to catch exceptions in the loop such that one report failure doesn't fail others.

Span spanToAdjust = span;
for (SpanAdjuster spanAdjuster : this.spanAdjusters) {
spanToAdjust = spanAdjuster.adjust(spanToAdjust);
}
delegate.report(spanToAdjust);
for (Reporter<zipkin2.Span> spanReporter : this.spanReporters) {
spanReporter.report(spanToAdjust);
}
};
}

Expand Down Expand Up @@ -173,6 +180,12 @@ CurrentTraceContext.Builder sleuthCurrentTraceContextBuilder() {
return ThreadLocalCurrentTraceContext.newBuilder();
}

@Bean
@ConditionalOnMissingBean
ReporterMetrics sleuthReporterMetrics() {
return new InMemoryReporterMetrics();
}

@Bean
@ConditionalOnMissingBean
Reporter<zipkin2.Span> noOpSpanReporter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
Expand All @@ -19,14 +19,13 @@
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.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
Expand Down Expand Up @@ -55,40 +54,41 @@
* {@link DefaultZipkinRestTemplateCustomizer} adds the GZip compression.
*
* @author Spencer Gibb
* @author Tim Ysewyn
* @since 1.0.0
* @see SamplerAutoConfiguration
* @see ZipkinRestTemplateCustomizer
* @see DefaultZipkinRestTemplateCustomizer
*/
@Configuration
@EnableConfigurationProperties(ZipkinProperties.class)
@ConditionalOnProperty(value = "spring.zipkin.enabled", matchIfMissing = true)
@ConditionalOnProperty(value = { "spring.sleuth.enabled",
"spring.zipkin.enabled" }, matchIfMissing = true)
@AutoConfigureBefore(TraceAutoConfiguration.class)
@AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration")
@Import({ ZipkinSenderConfigurationImportSelector.class, SamplerAutoConfiguration.class })
public class ZipkinAutoConfiguration {

/**
* Accepts a sender so you can plug-in any standard one. Returns a Reporter so you can
* also replace with a standard one.
* Zipkin reporter bean name. Name of the bean matters for supporting multiple tracing
* systems.
*/
@Bean
@ConditionalOnMissingBean
public static final String REPORTER_BEAN_NAME = "zipkinReporter";

/**
* Zipkin sender bean name. Name of the bean matters for supporting multiple tracing
* systems.
*/
public static final String SENDER_BEAN_NAME = "zipkinSender";

@Bean(REPORTER_BEAN_NAME)
@ConditionalOnMissingBean(name = REPORTER_BEAN_NAME)
public Reporter<Span> reporter(ReporterMetrics reporterMetrics,
ZipkinProperties zipkin, Sender sender, BytesEncoder<Span> spanBytesEncoder) {
return AsyncReporter.builder(sender).queuedMaxSpans(1000) // historical
// constraint. Note:
// AsyncReporter
// supports memory
// bounds
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<Span> spanBytesEncoder(ZipkinProperties zipkinProperties) {
return zipkinProperties.getEncoder();
.metrics(reporterMetrics).build(zipkin.getEncoder());
}

@Bean
Expand All @@ -98,12 +98,6 @@ public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(
return new DefaultZipkinRestTemplateCustomizer(zipkinProperties);
}

@Bean
@ConditionalOnMissingBean
ReporterMetrics sleuthReporterMetrics() {
return new InMemoryReporterMetrics();
}

@Configuration
@ConditionalOnMissingBean(EndpointLocator.class)
@ConditionalOnProperty(value = "spring.zipkin.locator.discovery.enabled", havingValue = "false", matchIfMissing = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* 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.List;
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> span reporter} if needed.
*
* @author Tim Ysewyn
* @since 2.1.0
* @see ZipkinAutoConfiguration
* @deprecated
*/
@Configuration
@ConditionalOnProperty(value = { "spring.sleuth.enabled" }, matchIfMissing = true)
@AutoConfigureAfter({ ZipkinAutoConfiguration.class })
@Deprecated
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
Reporter<Span> reporter(ReporterMetrics reporterMetrics, ZipkinProperties zipkin,
BytesEncoder<Span> spanBytesEncoder, DefaultListableBeanFactory beanFactory) {
List<String> 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
BytesEncoder<Span> 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();
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
Expand All @@ -25,6 +25,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
Expand All @@ -34,15 +35,15 @@
@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 {

@Value("${spring.zipkin.kafka.topic:zipkin}")
private String topic;

@Bean
@Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME)
Sender kafkaSender(KafkaProperties config) {
Map<String, Object> properties = config.buildProducerProperties();
properties.put("key.serializer", ByteArraySerializer.class.getName());
Expand Down
Loading