Drops zipkin dependency from spring-cloud-sleuth-core (#1649)
There was emmense work to prepare for decoupling of spring-cloud-sleuth-core from Zipkin. This included complete test conversion and deprecations between 2.2.x and 3.0.x. This moves all Zipkin related code to spring-cloud-sleuth-zipkin, making the primary data recording tool `SpanHandler` as opposed to `Reporter<zipkin2.Span>` For example, Wavefront and soon Stackdriver can implement `SpanHandler` and skip conversion into the Zipkin model first. `SpanHandler` also includes begin and end hooks which allow data extensions to be developed. see https://github.com/wavefrontHQ/wavefront-spring-boot
This commit is contained in:
@@ -184,6 +184,16 @@
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.zipkin.zipkin2</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.autoconfig;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import brave.CurrentSpanCustomizer;
|
||||
import brave.ErrorParser;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.TracingCustomizer;
|
||||
@@ -32,20 +29,8 @@ import brave.propagation.CurrentTraceContextCustomizer;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.propagation.ThreadLocalCurrentTraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import zipkin2.Span;
|
||||
import zipkin2.reporter.InMemoryReporterMetrics;
|
||||
import zipkin2.reporter.Reporter;
|
||||
import zipkin2.reporter.ReporterMetrics;
|
||||
import zipkin2.reporter.brave.ZipkinSpanHandler;
|
||||
import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics;
|
||||
|
||||
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.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.LocalServiceName;
|
||||
@@ -85,38 +70,17 @@ public class TraceAutoConfiguration {
|
||||
*/
|
||||
public static final String DEFAULT_SERVICE_NAME = "default";
|
||||
|
||||
/**
|
||||
* Sort Zipkin Handlers last, so that redactions etc happen prior.
|
||||
*/
|
||||
static final Comparator<SpanHandler> SPAN_HANDLER_COMPARATOR = (o1, o2) -> {
|
||||
if (o1 instanceof ZipkinSpanHandler) {
|
||||
if (o2 instanceof ZipkinSpanHandler) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (o2 instanceof ZipkinSpanHandler) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
// NOTE: stable bean name as might be used outside sleuth
|
||||
Tracing tracing(@LocalServiceName String serviceName, Propagation.Factory factory,
|
||||
CurrentTraceContext currentTraceContext, Sampler sampler,
|
||||
ErrorParser errorParser, SleuthProperties sleuthProperties,
|
||||
@Nullable List<Reporter<zipkin2.Span>> spanReporters,
|
||||
@Nullable List<SpanHandler> spanHandlers,
|
||||
SleuthProperties sleuthProperties, @Nullable List<SpanHandler> spanHandlers,
|
||||
@Nullable List<TracingCustomizer> tracingCustomizers) {
|
||||
Tracing.Builder builder = Tracing.newBuilder().sampler(sampler)
|
||||
.errorParser(errorParser)
|
||||
.localServiceName(StringUtils.isEmpty(serviceName) ? DEFAULT_SERVICE_NAME
|
||||
: serviceName)
|
||||
.propagationFactory(factory).currentTraceContext(currentTraceContext)
|
||||
.spanReporter(new CompositeReporter(
|
||||
spanReporters != null ? spanReporters : Collections.emptyList()))
|
||||
.traceId128Bit(sleuthProperties.isTraceId128())
|
||||
.supportsJoin(sleuthProperties.isSupportsJoin());
|
||||
if (spanHandlers != null) {
|
||||
@@ -130,20 +94,9 @@ public class TraceAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
reorderZipkinHandlersLast(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void reorderZipkinHandlersLast(Tracing.Builder builder) {
|
||||
List<SpanHandler> configuredSpanHandlers = new ArrayList<>(
|
||||
builder.spanHandlers());
|
||||
configuredSpanHandlers.sort(SPAN_HANDLER_COMPARATOR);
|
||||
builder.clearSpanHandlers();
|
||||
for (SpanHandler spanHandler : configuredSpanHandlers) {
|
||||
builder.addSpanHandler(spanHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean(name = TRACER_BEAN_NAME)
|
||||
@ConditionalOnMissingBean
|
||||
Tracer tracer(Tracing tracing) {
|
||||
@@ -182,18 +135,6 @@ public class TraceAutoConfiguration {
|
||||
return ThreadLocalCurrentTraceContext.newBuilder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
Reporter<zipkin2.Span> noOpSpanReporter() {
|
||||
return Reporter.NOOP;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ErrorParser errorParser() {
|
||||
return new ErrorParser();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
// NOTE: stable bean name as might be used outside sleuth
|
||||
@@ -201,91 +142,4 @@ public class TraceAutoConfiguration {
|
||||
return CurrentSpanCustomizer.create(tracing);
|
||||
}
|
||||
|
||||
private static final class CompositeReporter implements Reporter<zipkin2.Span> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(CompositeReporter.class);
|
||||
|
||||
private final Reporter<zipkin2.Span> spanReporter;
|
||||
|
||||
private CompositeReporter(List<Reporter<Span>> spanReporters) {
|
||||
this.spanReporter = spanReporters.size() == 1 ? spanReporters.get(0)
|
||||
: new ListReporter(spanReporters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
this.spanReporter.report(span);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CompositeReporter{ spanReporters=" + this.spanReporter + '}';
|
||||
}
|
||||
|
||||
private static final class ListReporter implements Reporter<zipkin2.Span> {
|
||||
|
||||
private final List<Reporter<Span>> spanReporters;
|
||||
|
||||
private ListReporter(List<Reporter<Span>> spanReporters) {
|
||||
this.spanReporters = spanReporters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
for (Reporter<zipkin2.Span> spanReporter : this.spanReporters) {
|
||||
try {
|
||||
spanReporter.report(span);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.warn("Exception occurred while trying to report the span "
|
||||
+ span, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ListReporter{" + "spanReporters=" + this.spanReporters + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry")
|
||||
static class TraceMetricsInMemoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ReporterMetrics sleuthReporterMetrics() {
|
||||
return new InMemoryReporterMetrics();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(MeterRegistry.class)
|
||||
static class TraceMetricsMicrometerConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(ReporterMetrics.class)
|
||||
static class NoReporterMetricsBeanConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(MeterRegistry.class)
|
||||
ReporterMetrics sleuthMicrometerReporterMetrics(MeterRegistry meterRegistry) {
|
||||
return MicrometerReporterMetrics.create(meterRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MeterRegistry.class)
|
||||
ReporterMetrics sleuthReporterMetrics() {
|
||||
return new InMemoryReporterMetrics();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -312,8 +312,8 @@ public class SleuthSpanCreatorAspectFluxTests {
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
then(this.spans).hasSize(1);
|
||||
then(this.spans.get(0).name()).isEqualTo("test-method12");
|
||||
then(this.spans.get(0).tags()).containsEntry("testTag12", "test")
|
||||
.containsEntry("error", "test exception 12");
|
||||
then(this.spans.get(0).tags()).containsEntry("testTag12", "test");
|
||||
then(this.spans.get(0).error()).hasMessageContaining("test exception 12");
|
||||
then(this.spans.get(0).finishTimestamp()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
});
|
||||
@@ -341,7 +341,7 @@ public class SleuthSpanCreatorAspectFluxTests {
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
then(this.spans).hasSize(1);
|
||||
then(this.spans.get(0).name()).isEqualTo("foo");
|
||||
then(this.spans.get(0).tags()).containsEntry("error", "test exception 13");
|
||||
then(this.spans.get(0).error()).hasMessageContaining("test exception 13");
|
||||
then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue)
|
||||
.collect(Collectors.toList())).contains("testMethod13.before",
|
||||
"testMethod13.afterFailure", "testMethod13.after");
|
||||
|
||||
@@ -325,8 +325,8 @@ public class SleuthSpanCreatorAspectMonoTests {
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
then(this.spans).hasSize(1);
|
||||
then(this.spans.get(0).name()).isEqualTo("test-method12");
|
||||
then(this.spans.get(0).tags()).containsEntry("testTag12", "test")
|
||||
.containsEntry("error", "test exception 12");
|
||||
then(this.spans.get(0).tags()).containsEntry("testTag12", "test");
|
||||
then(this.spans.get(0).error()).hasMessageContaining("test exception 12");
|
||||
then(this.spans.get(0).finishTimestamp()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
});
|
||||
@@ -354,7 +354,7 @@ public class SleuthSpanCreatorAspectMonoTests {
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
then(this.spans).hasSize(1);
|
||||
then(this.spans.get(0).name()).isEqualTo("foo");
|
||||
then(this.spans.get(0).tags()).containsEntry("error", "test exception 13");
|
||||
then(this.spans.get(0).error()).hasMessageContaining("test exception 13");
|
||||
then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue)
|
||||
.collect(Collectors.toList())).contains("testMethod13.before",
|
||||
"testMethod13.afterFailure", "testMethod13.after");
|
||||
|
||||
@@ -233,8 +233,8 @@ public class SleuthSpanCreatorAspectTests {
|
||||
|
||||
then(this.spans).hasSize(1);
|
||||
then(this.spans.get(0).name()).isEqualTo("test-method12");
|
||||
then(this.spans.get(0).tags()).containsEntry("testTag12", "test")
|
||||
.containsEntry("error", "test exception 12");
|
||||
then(this.spans.get(0).tags()).containsEntry("testTag12", "test");
|
||||
then(this.spans.get(0).error()).hasMessageContaining("test exception 12");
|
||||
then(this.spans.get(0).finishTimestamp()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
@@ -256,7 +256,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
|
||||
then(this.spans).hasSize(1);
|
||||
then(this.spans.get(0).name()).isEqualTo("foo");
|
||||
then(this.spans.get(0).tags()).containsEntry("error", "test exception 13");
|
||||
then(this.spans.get(0).error()).hasMessageContaining("test exception 13");
|
||||
then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue)
|
||||
.collect(Collectors.toList())).contains("testMethod13.before",
|
||||
"testMethod13.afterFailure", "testMethod13.after");
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.autoconfig;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import brave.Tracing;
|
||||
@@ -24,9 +23,11 @@ import brave.baggage.BaggageField;
|
||||
import brave.baggage.BaggagePropagation;
|
||||
import brave.baggage.BaggagePropagationConfig.SingleBaggageField;
|
||||
import brave.baggage.BaggagePropagationCustomizer;
|
||||
import brave.handler.MutableSpan;
|
||||
import brave.handler.SpanHandler;
|
||||
import brave.propagation.B3SinglePropagation;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.propagation.TraceContextOrSamplingFlags;
|
||||
import brave.sampler.RateLimitingSampler;
|
||||
import brave.sampler.Sampler;
|
||||
@@ -34,76 +35,18 @@ import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import zipkin2.reporter.InMemoryReporterMetrics;
|
||||
import zipkin2.reporter.Reporter;
|
||||
import zipkin2.reporter.ReporterMetrics;
|
||||
import zipkin2.reporter.brave.ZipkinSpanHandler;
|
||||
import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
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;
|
||||
import static org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration.SPAN_HANDLER_COMPARATOR;
|
||||
|
||||
public class TraceAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void span_handler_comparator() {
|
||||
SpanHandler handler1 = mock(SpanHandler.class);
|
||||
SpanHandler handler2 = mock(SpanHandler.class);
|
||||
ZipkinSpanHandler zipkin1 = mock(ZipkinSpanHandler.class);
|
||||
ZipkinSpanHandler zipkin2 = mock(ZipkinSpanHandler.class);
|
||||
|
||||
ArrayList<SpanHandler> spanHandlers = new ArrayList<>();
|
||||
spanHandlers.add(handler1);
|
||||
spanHandlers.add(zipkin1);
|
||||
spanHandlers.add(handler2);
|
||||
spanHandlers.add(zipkin2);
|
||||
|
||||
spanHandlers.sort(SPAN_HANDLER_COMPARATOR);
|
||||
|
||||
assertThat(spanHandlers).containsExactly(handler1, handler2, zipkin1, zipkin2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_apply_micrometer_reporter_metrics_when_meter_registry_bean_present() {
|
||||
this.contextRunner.withUserConfiguration(WithMeterRegistry.class)
|
||||
.run((context) -> {
|
||||
ReporterMetrics bean = context.getBean(ReporterMetrics.class);
|
||||
|
||||
BDDAssertions.then(bean)
|
||||
.isInstanceOf(MicrometerReporterMetrics.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_apply_in_memory_metrics_when_meter_registry_bean_missing() {
|
||||
this.contextRunner.run((context) -> {
|
||||
ReporterMetrics bean = context.getBean(ReporterMetrics.class);
|
||||
|
||||
BDDAssertions.then(bean).isInstanceOf(InMemoryReporterMetrics.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_apply_in_memory_metrics_when_meter_registry_class_missing() {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(MeterRegistry.class))
|
||||
.run((context) -> {
|
||||
ReporterMetrics bean = context.getBean(ReporterMetrics.class);
|
||||
|
||||
BDDAssertions.then(bean).isInstanceOf(InMemoryReporterMetrics.class);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates
|
||||
* {@link org.springframework.cloud.sleuth.sampler.SamplerAutoConfigurationTests}
|
||||
@@ -123,8 +66,8 @@ public class TraceAutoConfigurationTests {
|
||||
* intentionally, to ensure configuration condition bugs do not exist.
|
||||
*/
|
||||
@Test
|
||||
void should_use_RateLimitedSampler_when_reporting() {
|
||||
this.contextRunner.withUserConfiguration(WithReporter.class).run((context -> {
|
||||
void should_use_RateLimitedSampler_withSpanHandler() {
|
||||
this.contextRunner.withUserConfiguration(WithSpanHandler.class).run((context -> {
|
||||
final Sampler bean = context.getBean(Sampler.class);
|
||||
BDDAssertions.then(bean).isInstanceOf(RateLimitingSampler.class);
|
||||
}));
|
||||
@@ -137,11 +80,10 @@ public class TraceAutoConfigurationTests {
|
||||
*/
|
||||
@Test
|
||||
void should_override_sampler() {
|
||||
this.contextRunner.withUserConfiguration(WithReporter.class, WithSampler.class)
|
||||
.run((context -> {
|
||||
final Sampler bean = context.getBean(Sampler.class);
|
||||
BDDAssertions.then(bean).isSameAs(Sampler.ALWAYS_SAMPLE);
|
||||
}));
|
||||
this.contextRunner.withUserConfiguration(WithSampler.class).run((context -> {
|
||||
final Sampler bean = context.getBean(Sampler.class);
|
||||
BDDAssertions.then(bean).isSameAs(Sampler.ALWAYS_SAMPLE);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -231,21 +173,16 @@ public class TraceAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WithMeterRegistry {
|
||||
static class WithSpanHandler {
|
||||
|
||||
@Bean
|
||||
MeterRegistry meterRegistry() {
|
||||
return new SimpleMeterRegistry();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WithReporter {
|
||||
|
||||
@Bean
|
||||
Reporter<zipkin2.Span> spanReporter() {
|
||||
return zipkin2.Span::toString;
|
||||
SpanHandler testSpanHandler() {
|
||||
return new SpanHandler() {
|
||||
@Override
|
||||
public boolean end(TraceContext context, MutableSpan span, Cause cause) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public class WebClientDiscoveryExceptionTests {
|
||||
// hystrix commands should finish at this point
|
||||
Thread.sleep(200);
|
||||
then(this.spans.spans().stream().filter(span1 -> span1.kind() == Span.Kind.CLIENT)
|
||||
.findFirst().get().tags()).containsKey("error");
|
||||
.findFirst().get().error()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -98,7 +98,7 @@ public class WebClientExceptionTests {
|
||||
|
||||
then(this.tracer.tracer().currentSpan()).isNull();
|
||||
then(this.spans).isNotEmpty();
|
||||
then(this.spans.get(0).tags()).containsKey("error");
|
||||
then(this.spans.get(0).error()).isNotNull();
|
||||
}
|
||||
|
||||
static Stream<Object> parametersForShouldCloseSpanUponException() {
|
||||
|
||||
@@ -25,8 +25,6 @@ import brave.sampler.RateLimitingSampler;
|
||||
import brave.sampler.Sampler;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import zipkin2.Span;
|
||||
import zipkin2.reporter.Reporter;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
@@ -59,14 +57,6 @@ public class SamplerAutoConfigurationTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_use_RateLimitedSampler_withReporter() {
|
||||
this.contextRunner.withUserConfiguration(WithReporter.class).run((context -> {
|
||||
final Sampler bean = context.getBean(Sampler.class);
|
||||
BDDAssertions.then(bean).isInstanceOf(RateLimitingSampler.class);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_use_RateLimitedSampler_withTracingCustomizer() {
|
||||
this.contextRunner.withUserConfiguration(WithTracingCustomizer.class)
|
||||
@@ -76,15 +66,6 @@ public class SamplerAutoConfigurationTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_override_sampler() {
|
||||
this.contextRunner.withUserConfiguration(WithReporter.class, WithSampler.class)
|
||||
.run((context -> {
|
||||
final Sampler bean = context.getBean(Sampler.class);
|
||||
BDDAssertions.then(bean).isSameAs(Sampler.ALWAYS_SAMPLE);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void samplerFromProps_probability() {
|
||||
SamplerProperties properties = new SamplerProperties();
|
||||
@@ -151,26 +132,6 @@ public class SamplerAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WithReporter {
|
||||
|
||||
@Bean
|
||||
Reporter<Span> spanReporter() {
|
||||
return zipkin2.Span::toString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WithSampler {
|
||||
|
||||
@Bean
|
||||
Sampler alwaysSampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WithTracingCustomizer {
|
||||
|
||||
|
||||
@@ -16,24 +16,38 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.zipkin2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import brave.Tag;
|
||||
import brave.TracingCustomizer;
|
||||
import brave.handler.SpanHandler;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import zipkin2.CheckResult;
|
||||
import zipkin2.Span;
|
||||
import zipkin2.reporter.AsyncReporter;
|
||||
import zipkin2.reporter.InMemoryReporterMetrics;
|
||||
import zipkin2.reporter.Reporter;
|
||||
import zipkin2.reporter.ReporterMetrics;
|
||||
import zipkin2.reporter.Sender;
|
||||
import zipkin2.reporter.brave.ZipkinSpanHandler;
|
||||
import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics;
|
||||
|
||||
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.ConditionalOnBean;
|
||||
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;
|
||||
@@ -45,6 +59,7 @@ 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.lang.Nullable;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
@@ -74,6 +89,22 @@ public class ZipkinAutoConfiguration {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ZipkinAutoConfiguration.class);
|
||||
|
||||
/**
|
||||
* Sort Zipkin Handlers last, so that redactions etc happen prior.
|
||||
*/
|
||||
static final Comparator<SpanHandler> SPAN_HANDLER_COMPARATOR = (o1, o2) -> {
|
||||
if (o1 instanceof ZipkinSpanHandler) {
|
||||
if (o2 instanceof ZipkinSpanHandler) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (o2 instanceof ZipkinSpanHandler) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Zipkin reporter bean name. Name of the bean matters for supporting multiple tracing
|
||||
* systems.
|
||||
@@ -143,6 +174,44 @@ public class ZipkinAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns one handler for as many reporters as exist. */
|
||||
@Bean
|
||||
SpanHandler zipkinSpanHandler(@Nullable List<Reporter<Span>> spanReporters,
|
||||
@Nullable Tag<Throwable> errorTag) {
|
||||
if (spanReporters == null) {
|
||||
return SpanHandler.NOOP;
|
||||
}
|
||||
|
||||
LinkedHashSet<Reporter<Span>> reporters = new LinkedHashSet<>(spanReporters);
|
||||
reporters.remove(Reporter.NOOP);
|
||||
if (spanReporters.isEmpty()) {
|
||||
return SpanHandler.NOOP;
|
||||
}
|
||||
|
||||
Reporter<Span> spanReporter = reporters.size() == 1 ? reporters.iterator().next()
|
||||
: new CompositeSpanReporter(reporters.toArray(new Reporter[0]));
|
||||
|
||||
ZipkinSpanHandler.Builder builder = ZipkinSpanHandler.newBuilder(spanReporter);
|
||||
if (errorTag != null) {
|
||||
builder.errorTag(errorTag);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/** This ensures Zipkin reporters end up after redaction, etc. */
|
||||
@Bean
|
||||
TracingCustomizer reorderZipkinHandlersLast() {
|
||||
return builder -> {
|
||||
List<SpanHandler> configuredSpanHandlers = new ArrayList<>(
|
||||
builder.spanHandlers());
|
||||
configuredSpanHandlers.sort(SPAN_HANDLER_COMPARATOR);
|
||||
builder.clearSpanHandlers();
|
||||
for (SpanHandler spanHandler : configuredSpanHandlers) {
|
||||
builder.addSpanHandler(spanHandler);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(
|
||||
@@ -206,4 +275,84 @@ public class ZipkinAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry")
|
||||
static class TraceMetricsInMemoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ReporterMetrics sleuthReporterMetrics() {
|
||||
return new InMemoryReporterMetrics();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(MeterRegistry.class)
|
||||
static class TraceMetricsMicrometerConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(ReporterMetrics.class)
|
||||
static class NoReporterMetricsBeanConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(MeterRegistry.class)
|
||||
ReporterMetrics sleuthMicrometerReporterMetrics(MeterRegistry meterRegistry) {
|
||||
return MicrometerReporterMetrics.create(meterRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MeterRegistry.class)
|
||||
ReporterMetrics sleuthReporterMetrics() {
|
||||
return new InMemoryReporterMetrics();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Zipkin conversion only happens once per mutable span
|
||||
static final class CompositeSpanReporter implements Reporter<Span> {
|
||||
|
||||
final Reporter<Span>[] reporters;
|
||||
|
||||
CompositeSpanReporter(Reporter<Span>[] reporters) {
|
||||
this.reporters = reporters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
for (Reporter<Span> reporter : reporters) {
|
||||
try {
|
||||
reporter.report(span);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
// TODO: message lifted from ListReporter: this is probably too much
|
||||
// for warn level
|
||||
log.warn("Exception occurred while trying to report the span " + span,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(reporters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof CompositeSpanReporter)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(((CompositeSpanReporter) obj).reporters, reporters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Arrays.toString(reporters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.sleuth.zipkin2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@@ -26,8 +27,11 @@ import brave.handler.MutableSpan;
|
||||
import brave.handler.SpanHandler;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -36,17 +40,24 @@ import zipkin2.Call;
|
||||
import zipkin2.CheckResult;
|
||||
import zipkin2.codec.Encoding;
|
||||
import zipkin2.reporter.AsyncReporter;
|
||||
import zipkin2.reporter.InMemoryReporterMetrics;
|
||||
import zipkin2.reporter.Reporter;
|
||||
import zipkin2.reporter.ReporterMetrics;
|
||||
import zipkin2.reporter.Sender;
|
||||
import zipkin2.reporter.activemq.ActiveMQSender;
|
||||
import zipkin2.reporter.amqp.RabbitMQSender;
|
||||
import zipkin2.reporter.brave.ZipkinSpanHandler;
|
||||
import zipkin2.reporter.kafka.KafkaSender;
|
||||
import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -57,6 +68,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration.SPAN_HANDLER_COMPARATOR;
|
||||
|
||||
/**
|
||||
* Not using {@linkplain SpringBootTest} as we need to change properties per test.
|
||||
@@ -65,6 +77,9 @@ import static org.mockito.Mockito.when;
|
||||
*/
|
||||
public class ZipkinAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ZipkinAutoConfiguration.class));
|
||||
|
||||
public MockWebServer server = new MockWebServer();
|
||||
|
||||
@BeforeEach
|
||||
@@ -89,7 +104,55 @@ public class ZipkinAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultsToV2Endpoint() throws Exception {
|
||||
void span_handler_comparator() {
|
||||
SpanHandler handler1 = mock(SpanHandler.class);
|
||||
SpanHandler handler2 = mock(SpanHandler.class);
|
||||
ZipkinSpanHandler zipkin1 = mock(ZipkinSpanHandler.class);
|
||||
ZipkinSpanHandler zipkin2 = mock(ZipkinSpanHandler.class);
|
||||
|
||||
ArrayList<SpanHandler> spanHandlers = new ArrayList<>();
|
||||
spanHandlers.add(handler1);
|
||||
spanHandlers.add(zipkin1);
|
||||
spanHandlers.add(handler2);
|
||||
spanHandlers.add(zipkin2);
|
||||
|
||||
spanHandlers.sort(SPAN_HANDLER_COMPARATOR);
|
||||
|
||||
assertThat(spanHandlers).containsExactly(handler1, handler2, zipkin1, zipkin2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_apply_micrometer_reporter_metrics_when_meter_registry_bean_present() {
|
||||
this.contextRunner.withUserConfiguration(WithMeterRegistry.class)
|
||||
.run((context) -> {
|
||||
ReporterMetrics bean = context.getBean(ReporterMetrics.class);
|
||||
|
||||
BDDAssertions.then(bean)
|
||||
.isInstanceOf(MicrometerReporterMetrics.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_apply_in_memory_metrics_when_meter_registry_bean_missing() {
|
||||
this.contextRunner.run((context) -> {
|
||||
ReporterMetrics bean = context.getBean(ReporterMetrics.class);
|
||||
|
||||
BDDAssertions.then(bean).isInstanceOf(InMemoryReporterMetrics.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_apply_in_memory_metrics_when_meter_registry_class_missing() {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(MeterRegistry.class))
|
||||
.run((context) -> {
|
||||
ReporterMetrics bean = context.getBean(ReporterMetrics.class);
|
||||
|
||||
BDDAssertions.then(bean).isInstanceOf(InMemoryReporterMetrics.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultsToV2Endpoint() throws Exception {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
environment().setProperty("spring.zipkin.base-url",
|
||||
this.server.url("/").toString());
|
||||
@@ -380,7 +443,7 @@ public class ZipkinAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class HandlerHanldersConfig {
|
||||
protected static class HandlersConfig {
|
||||
|
||||
@Bean
|
||||
SpanHandler handlerOne() {
|
||||
@@ -408,6 +471,26 @@ public class ZipkinAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WithMeterRegistry {
|
||||
|
||||
@Bean
|
||||
MeterRegistry meterRegistry() {
|
||||
return new SimpleMeterRegistry();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WithReporter {
|
||||
|
||||
@Bean
|
||||
Reporter<zipkin2.Span> spanReporter() {
|
||||
return zipkin2.Span::toString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class MultipleReportersConfig {
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
// we need to dump the span cause it's not in TracingFilter since TF
|
||||
// has also error dispatch and the ErrorController would report the span
|
||||
then(this.spans).hasSize(1);
|
||||
then(this.spans.get(0).tags()).containsEntry("error",
|
||||
then(this.spans.get(0).error()).hasMessageContaining(
|
||||
"Request processing failed; nested exception is java.lang.RuntimeException");
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ public class TraceFilterWebIntegrationTests {
|
||||
}
|
||||
|
||||
then(this.currentTraceContext.get()).isNull();
|
||||
MutableSpan fromFirstTraceFilterFlow = spanHandler.takeRemoteSpanWithErrorTag(
|
||||
MutableSpan fromFirstTraceFilterFlow = spanHandler.takeRemoteSpanWithErrorMessage(
|
||||
Kind.SERVER,
|
||||
"Request processing failed; nested exception is java.lang.RuntimeException: Throwing exception");
|
||||
then(fromFirstTraceFilterFlow.tags()).containsEntry("http.method", "GET")
|
||||
|
||||
Reference in New Issue
Block a user