Support Brave's joint spans

Brave has a feature called joint spans that was enabled by default with
Spring Cloud Sleuth (and Boot 2.x). This has changed with
Micrometer Tracing and Boot 3.x. In order to make migration simpler,
joint spans should be set through a property.
This is a Brave+B3-only feature, W3C is not supported by Brave and
OTel does not have this capability.

See gh-35165
This commit is contained in:
Jonatan Ivanov
2023-04-25 13:46:19 -07:00
parent 8494ad82cd
commit 5142e8e157
3 changed files with 75 additions and 7 deletions

View File

@@ -54,12 +54,14 @@ import io.micrometer.tracing.exporter.SpanFilter;
import io.micrometer.tracing.exporter.SpanReporter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.tracing.TracingProperties.Propagation.PropagationType;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.IncompatibleConfigurationException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
@@ -71,6 +73,7 @@ import org.springframework.core.env.Environment;
*
* @author Moritz Halbritter
* @author Marcin Grzejszczak
* @author Jonatan Ivanov
* @since 3.0.0
*/
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@@ -97,14 +100,19 @@ public class BraveAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public Tracing braveTracing(Environment environment, List<SpanHandler> spanHandlers,
public Tracing braveTracing(Environment environment, TracingProperties properties, List<SpanHandler> spanHandlers,
List<TracingCustomizer> tracingCustomizers, CurrentTraceContext currentTraceContext,
Factory propagationFactory, Sampler sampler) {
if (properties.getPropagation().getType() == PropagationType.W3C
&& properties.getBrave().isSpanJoiningSupported()) {
throw new IncompatibleConfigurationException("management.tracing.propagation.type",
"management.tracing.brave.span-joining-supported");
}
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
Builder builder = Tracing.newBuilder()
.currentTraceContext(currentTraceContext)
.traceId128Bit(true)
.supportsJoin(false)
.supportsJoin(properties.getBrave().isSpanJoiningSupported())
.propagationFactory(propagationFactory)
.sampler(sampler)
.localServiceName(applicationName);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 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.
@@ -25,6 +25,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* Configuration properties for tracing.
*
* @author Moritz Halbritter
* @author Jonatan Ivanov
* @since 3.0.0
*/
@ConfigurationProperties("management.tracing")
@@ -50,6 +51,11 @@ public class TracingProperties {
*/
private final Propagation propagation = new Propagation();
/**
* Brave configuration.
*/
private final Brave brave = new Brave();
public boolean isEnabled() {
return this.enabled;
}
@@ -70,6 +76,10 @@ public class TracingProperties {
return this.propagation;
}
public Brave getBrave() {
return this.brave;
}
public static class Sampling {
/**
@@ -194,4 +204,23 @@ public class TracingProperties {
}
public static class Brave {
/**
* Whether the propagation type and tracing backend support sharing the span ID
* between client and server spans. Requires B3 propagation and a compatible
* backend.
*/
private boolean spanJoiningSupported = false;
public boolean isSpanJoiningSupported() {
return this.spanJoiningSupported;
}
public void setSpanJoiningSupported(boolean spanJoiningSupported) {
this.spanJoiningSupported = spanJoiningSupported;
}
}
}

View File

@@ -43,6 +43,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfigurationTests.SpanHandlerConfiguration.AdditionalSpanHandler;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.properties.IncompatibleConfigurationException;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@@ -50,12 +51,14 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link BraveAutoConfiguration}.
*
* @author Moritz Halbritter
* @author Jonatan Ivanov
*/
class BraveAutoConfigurationTests {
@@ -216,17 +219,45 @@ class BraveAutoConfigurationTests {
}
@Test
void shouldNotSupportJoinedSpans() {
void shouldNotSupportJoinedSpansByDefault() {
this.contextRunner.run((context) -> {
Tracing tracing = context.getBean(Tracing.class);
Span parentSpan = tracing.tracer().nextSpan();
Span childSpan = tracing.tracer().joinSpan(parentSpan.context());
assertThat(parentSpan.context().traceIdString()).isEqualTo(childSpan.context().traceIdString());
assertThat(parentSpan.context().spanIdString()).isEqualTo(childSpan.context().parentIdString());
assertThat(parentSpan.context().spanIdString()).isNotEqualTo(childSpan.context().spanIdString());
assertThat(childSpan.context().traceIdString()).isEqualTo(parentSpan.context().traceIdString());
assertThat(childSpan.context().spanIdString()).isNotEqualTo(parentSpan.context().spanIdString());
assertThat(childSpan.context().parentIdString()).isEqualTo(parentSpan.context().spanIdString());
assertThat(parentSpan.context().parentIdString()).isNull();
});
}
@Test
void shouldSupportJoinedSpansIfB3UsedAndBackendSupportsIt() {
this.contextRunner
.withPropertyValues("management.tracing.propagation.type=B3",
"management.tracing.brave.span-joining-supported=true")
.run((context) -> {
Tracing tracing = context.getBean(Tracing.class);
Span parentSpan = tracing.tracer().nextSpan();
Span childSpan = tracing.tracer().joinSpan(parentSpan.context());
assertThat(childSpan.context().traceIdString()).isEqualTo(parentSpan.context().traceIdString());
assertThat(childSpan.context().spanIdString()).isEqualTo(parentSpan.context().spanIdString());
assertThat(childSpan.context().parentIdString()).isNull();
assertThat(parentSpan.context().parentIdString()).isNull();
});
}
@Test
void shouldFailIfSupportJoinedSpansIsEnabledAndW3cIsChosen() {
this.contextRunner
.withPropertyValues("management.tracing.propagation.type=W3C",
"management.tracing.brave.span-joining-supported=true")
.run((context) -> assertThatThrownBy(() -> context.getBean(Tracing.class)).rootCause()
.isExactlyInstanceOf(IncompatibleConfigurationException.class)
.hasMessage(
"The following configuration properties have incompatible values: [management.tracing.propagation.type, management.tracing.brave.span-joining-supported]"));
}
@Test
@SuppressWarnings("rawtypes")
void compositeSpanHandlerShouldBeFirstSpanHandler() {