Add support for MDC, Context Propagation (via B3 and W3C), and Baggage

See gh-32480
This commit is contained in:
Marcin Grzejszczak
2022-09-01 17:17:43 +02:00
committed by Andy Wilkinson
parent bf5bd4f91c
commit 52d1436dc6
7 changed files with 744 additions and 21 deletions

View File

@@ -22,6 +22,14 @@ import brave.Tracer;
import brave.Tracing;
import brave.Tracing.Builder;
import brave.TracingCustomizer;
import brave.baggage.BaggageField;
import brave.baggage.BaggagePropagation;
import brave.baggage.BaggagePropagationConfig;
import brave.baggage.BaggagePropagationCustomizer;
import brave.baggage.CorrelationScopeConfig;
import brave.baggage.CorrelationScopeCustomizer;
import brave.baggage.CorrelationScopeDecorator;
import brave.context.slf4j.MDCScopeDecorator;
import brave.handler.SpanHandler;
import brave.http.HttpClientHandler;
import brave.http.HttpClientRequest;
@@ -34,6 +42,7 @@ import brave.propagation.B3Propagation;
import brave.propagation.CurrentTraceContext;
import brave.propagation.CurrentTraceContext.ScopeDecorator;
import brave.propagation.CurrentTraceContextCustomizer;
import brave.propagation.Propagation;
import brave.propagation.Propagation.Factory;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.sampler.Sampler;
@@ -41,20 +50,28 @@ import io.micrometer.tracing.brave.bridge.BraveBaggageManager;
import io.micrometer.tracing.brave.bridge.BraveCurrentTraceContext;
import io.micrometer.tracing.brave.bridge.BraveHttpClientHandler;
import io.micrometer.tracing.brave.bridge.BraveHttpServerHandler;
import io.micrometer.tracing.brave.bridge.BravePropagator;
import io.micrometer.tracing.brave.bridge.BraveTracer;
import io.micrometer.tracing.brave.bridge.W3CPropagation;
import org.slf4j.MDC;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Brave.
*
* @author Moritz Halbritter
* @author Marcin Grzejszczak
* @since 3.0.0
*/
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@@ -63,6 +80,8 @@ import org.springframework.core.env.Environment;
@ConditionalOnEnabledTracing
public class BraveAutoConfiguration {
private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager();
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
@@ -105,12 +124,6 @@ public class BraveAutoConfiguration {
return builder.build();
}
@Bean
@ConditionalOnMissingBean
public Factory bravePropagationFactory() {
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
}
@Bean
@ConditionalOnMissingBean
public Sampler braveSampler(TracingProperties properties) {
@@ -137,15 +150,14 @@ public class BraveAutoConfiguration {
@Bean
@ConditionalOnMissingBean
BraveTracer braveTracerBridge(brave.Tracer tracer, CurrentTraceContext currentTraceContext,
BraveBaggageManager braveBaggageManager) {
return new BraveTracer(tracer, new BraveCurrentTraceContext(currentTraceContext), braveBaggageManager);
BraveTracer braveTracerBridge(brave.Tracer tracer, CurrentTraceContext currentTraceContext) {
return new BraveTracer(tracer, new BraveCurrentTraceContext(currentTraceContext), BRAVE_BAGGAGE_MANAGER);
}
@Bean
@ConditionalOnMissingBean
BraveBaggageManager braveBaggageManager() {
return new BraveBaggageManager();
BravePropagator bravePropagator(Tracing tracing) {
return new BravePropagator(tracing);
}
@Bean
@@ -162,4 +174,108 @@ public class BraveAutoConfiguration {
return new BraveHttpClientHandler(httpClientHandler);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false", matchIfMissing = true)
static class BraveNoBaggageConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C",
matchIfMissing = true)
Factory w3cPropagationNoBaggageFactory() {
return new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of()); // TODO: Use
// snapshots
// of
// tracing
// to not
// use
// baggage
// for W3C
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3")
Factory b3PropagationNoBaggageFactory() {
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true)
static class BraveBaggageConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C",
matchIfMissing = true)
BaggagePropagation.FactoryBuilder w3cPropagationFactory() {
return BaggagePropagation.newFactoryBuilder(new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of()));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3")
BaggagePropagation.FactoryBuilder b3PropagationFactory() {
return BaggagePropagation.newFactoryBuilder(
B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build());
}
@Bean
@ConditionalOnMissingBean
Propagation.Factory micrometerTracingPropagationWithBaggage(BaggagePropagation.FactoryBuilder factoryBuilder,
TracingProperties tracingProperties,
ObjectProvider<List<BaggagePropagationCustomizer>> baggagePropagationCustomizers) {
List<String> remoteFields = tracingProperties.getBaggage().getRemoteFields();
for (String fieldName : remoteFields) {
factoryBuilder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName)));
}
baggagePropagationCustomizers.ifAvailable(
(customizers) -> customizers.forEach((customizer) -> customizer.customize(factoryBuilder)));
return factoryBuilder.build();
}
@Bean
@ConditionalOnMissingBean(CorrelationScopeDecorator.class)
@ConditionalOnBean(CorrelationScopeDecorator.Builder.class)
@ConditionalOnProperty(value = "management.tracing.baggage.correlation.enabled", matchIfMissing = true)
ScopeDecorator correlationFieldsCorrelationScopeDecorator(TracingProperties properties,
ObjectProvider<List<CorrelationScopeCustomizer>> correlationScopeCustomizers,
CorrelationScopeDecorator.Builder builder) {
List<String> correlationFields = properties.getBaggage().getCorrelation().getFields();
for (String field : correlationFields) {
builder.add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(BaggageField.create(field))
.flushOnUpdate().build());
}
correlationScopeCustomizers
.ifAvailable((customizers) -> customizers.forEach((customizer) -> customizer.customize(builder)));
return builder.build();
}
@Bean
@ConditionalOnMissingBean(CorrelationScopeDecorator.class)
@ConditionalOnBean(CorrelationScopeDecorator.Builder.class)
@ConditionalOnProperty(value = "management.tracing.baggage.correlation.enabled", havingValue = "false")
ScopeDecorator noCorrelationFieldsCorrelationScopeDecorator(CorrelationScopeDecorator.Builder builder,
ObjectProvider<List<CorrelationScopeCustomizer>> correlationScopeCustomizers) {
correlationScopeCustomizers
.ifAvailable((customizers) -> customizers.forEach((customizer) -> customizer.customize(builder)));
return builder.build();
}
}
@Configuration(proxyBeanMethods = false)
static class CorrelationScopeDecoratorConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(MDC.class)
CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder() {
return MDCScopeDecorator.newBuilder();
}
}
}

View File

@@ -23,17 +23,27 @@ import java.util.stream.Collectors;
import io.micrometer.tracing.SamplerFunction;
import io.micrometer.tracing.otel.bridge.DefaultHttpClientAttributesGetter;
import io.micrometer.tracing.otel.bridge.DefaultHttpServerAttributesExtractor;
import io.micrometer.tracing.otel.bridge.EventListener;
import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper;
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
import io.micrometer.tracing.otel.bridge.OtelHttpClientHandler;
import io.micrometer.tracing.otel.bridge.OtelHttpServerHandler;
import io.micrometer.tracing.otel.bridge.OtelPropagator;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
@@ -43,14 +53,17 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.slf4j.MDC;
import org.springframework.boot.SpringBootVersion;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
@@ -103,7 +116,6 @@ public class OpenTelemetryAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean
SpanProcessor otelSpanProcessor(List<SpanExporter> spanExporter) {
return SpanProcessor.composite(spanExporter.stream()
.map((exporter) -> BatchSpanProcessor.builder(exporter).build()).collect(Collectors.toList()));
@@ -118,21 +130,27 @@ public class OpenTelemetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
OtelTracer micrometerOtelTracer(Tracer tracer, EventPublisher eventPublisher,
OtelCurrentTraceContext otelCurrentTraceContext) {
OtelCurrentTraceContext otelCurrentTraceContext, TracingProperties properties) {
return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher,
new OtelBaggageManager(otelCurrentTraceContext, List.of(), List.of()));
new OtelBaggageManager(otelCurrentTraceContext, properties.getBaggage().getRemoteFields(), List.of()));
}
@Bean
@ConditionalOnMissingBean
EventPublisher otelTracerEventPublisher() {
return (event) -> {
};
OtelPropagator otelPropagator(ContextPropagators contextPropagators, Tracer tracer) {
return new OtelPropagator(contextPropagators, tracer);
}
@Bean
@ConditionalOnMissingBean
OtelCurrentTraceContext otelCurrentTraceContext() {
EventPublisher otelTracerEventPublisher(List<EventListener> eventListeners) {
return new OTelEventPublisher(eventListeners);
}
@Bean
@ConditionalOnMissingBean
OtelCurrentTraceContext otelCurrentTraceContext(EventPublisher publisher) {
ContextStorage.addWrapper(new EventPublishingContextWrapper(publisher));
return new OtelCurrentTraceContext();
}
@@ -150,4 +168,112 @@ public class OpenTelemetryAutoConfiguration {
new DefaultHttpServerAttributesExtractor());
}
@Configuration(proxyBeanMethods = false)
static class PropagationConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(B3Propagator.class)
static class B3NoBaggagePropagatorConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3")
B3Propagator b3TextMapPropagator() {
return B3Propagator.injectingSingleHeader();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false")
static class W3CNoBaggagePropagatorConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C",
matchIfMissing = true)
W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() {
return W3CTraceContextPropagator.getInstance();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true)
static class W3CBaggagePropagatorConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "W3C",
matchIfMissing = true)
TextMapPropagator w3cTextMapPropagatorWithBaggage() {
return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(),
W3CBaggagePropagator.getInstance());
}
}
}
@Configuration(proxyBeanMethods = false)
static class MicrometerTracingPropagationConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true)
static class BaggagePropagatorConfiguration {
@Bean
@ConditionalOnProperty(value = "management.tracing.propagation.type", havingValue = "B3")
BaggageTextMapPropagator b3BaggageTextMapPropagator(TracingProperties properties,
OtelCurrentTraceContext otelCurrentTraceContext) {
return new BaggageTextMapPropagator(properties.getBaggage().getRemoteFields(), new OtelBaggageManager(
otelCurrentTraceContext, properties.getBaggage().getRemoteFields(), List.of()));
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MDC.class)
static class Slf4jConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "management.tracing.baggage.correlation.enabled", matchIfMissing = true)
Slf4JBaggageEventListener otelSlf4JBaggageEventListener(TracingProperties tracingProperties) {
return new Slf4JBaggageEventListener(tracingProperties.getBaggage().getCorrelation().getFields());
}
}
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MDC.class)
static class Slf4jConfiguration {
@Bean
@ConditionalOnMissingBean
Slf4JEventListener otelSlf4JEventListener() {
return new Slf4JEventListener();
}
}
static class OTelEventPublisher implements EventPublisher {
private final List<EventListener> listeners;
OTelEventPublisher(List<EventListener> listeners) {
this.listeners = listeners;
}
@Override
public void publishEvent(Object event) {
for (EventListener listener : this.listeners) {
listener.onEvent(event);
}
}
}
}

View File

@@ -16,6 +16,9 @@
package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@@ -32,10 +35,28 @@ public class TracingProperties {
*/
private final Sampling sampling = new Sampling();
/**
* Baggage configuration.
*/
private final Baggage baggage = new Baggage();
/**
* Propagation configuration.
*/
private final Propagation propagation = new Propagation();
public Sampling getSampling() {
return this.sampling;
}
public Baggage getBaggage() {
return this.baggage;
}
public Propagation getPropagation() {
return this.propagation;
}
public static class Sampling {
/**
@@ -53,4 +74,111 @@ public class TracingProperties {
}
public static class Baggage {
/**
* Whether to enable Micrometer Tracing baggage propagation.
*/
private boolean enabled;
/**
* Correlation configuration.
*/
private Correlation correlation = new Correlation();
/**
* List of fields that are referenced the same in-process as it is on the wire.
* For example, the field "x-vcap-request-id" would be set as-is including the
* prefix.
*/
private List<String> remoteFields = new ArrayList<>();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Correlation getCorrelation() {
return this.correlation;
}
public void setCorrelation(Correlation correlation) {
this.correlation = correlation;
}
public List<String> getRemoteFields() {
return this.remoteFields;
}
public void setRemoteFields(List<String> remoteFields) {
this.remoteFields = remoteFields;
}
public static class Correlation {
/**
* Whether to enable correlation of the baggage context with logging contexts.
*/
private boolean enabled = true;
/**
* List of fields that should be correlated with the logging context. That
* means that these fields would end up as key-value pairs in e.g. MDC.
*/
private List<String> fields = new ArrayList<>();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<String> getFields() {
return this.fields;
}
public void setFields(List<String> fields) {
this.fields = fields;
}
}
}
public static class Propagation {
/**
* Tracing context propagation types.
*/
private PropagationType type = PropagationType.W3C;
public PropagationType getType() {
return this.type;
}
public void setType(PropagationType type) {
this.type = type;
}
enum PropagationType {
/**
* B3 propagation type.
*/
B3,
/**
* W3C propagation type.
*/
W3C
}
}
}

View File

@@ -2086,6 +2086,10 @@
"replacement": "management.trace.http.include",
"level": "error"
}
},
{
"name": "management.tracing.propagation.type",
"defaultValue": "W3C"
}
],
"hints": [

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2012-2022 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
*
* https://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.boot.actuate.autoconfigure.tracing;
import java.util.function.Supplier;
import io.micrometer.tracing.BaggageInScope;
import io.micrometer.tracing.BaggageManager;
import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
import io.opentelemetry.context.Context;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for Baggage configuration.
*
* @author Marcin Grzejszczak
*/
class BaggageAutoConfigurationTests {
static final String COUNTRY_CODE = "country-code";
static final String BUSINESS_PROCESS = "bp";
@BeforeEach
@AfterEach
void setup() {
MDC.clear();
}
@ParameterizedTest
@EnumSource(AutoConfig.class)
void shouldSetEntriesToMdcFromSpanWithBaggage(AutoConfig autoConfig) {
autoConfig.get().run((context) -> {
Tracer tracer = tracer(context);
Span span = createSpan(tracer);
assertThatTracingContextIsInitialized(autoConfig);
try (Tracer.SpanInScope scope = tracer.withSpan(span.start());
BaggageInScope fo = context.getBean(BaggageManager.class).createBaggage(COUNTRY_CODE)
.set(span.context(), "FO");
BaggageInScope bp = context.getBean(BaggageManager.class).createBaggage(BUSINESS_PROCESS)
.set(span.context(), "ALM")) {
assertThat(MDC.get("traceId")).isEqualTo(span.context().traceId());
assertThat(MDC.get(COUNTRY_CODE)).isEqualTo("FO");
assertThat(MDC.get(BUSINESS_PROCESS)).isEqualTo("ALM");
}
finally {
span.end();
}
assertThatMdcContainsUnsetTraceId();
assertThat(MDC.get(COUNTRY_CODE)).isNull();
assertThat(MDC.get(BUSINESS_PROCESS)).isNull();
});
}
@ParameterizedTest
@EnumSource(AutoConfig.class)
void shouldRemoveEntriesFromMdcForNullSpan(AutoConfig autoConfig) {
autoConfig.get().run((context) -> {
Tracer tracer = tracer(context);
Span span = createSpan(tracer);
assertThatTracingContextIsInitialized(autoConfig);
try (Tracer.SpanInScope scope = tracer.withSpan(span.start());
BaggageInScope fo = context.getBean(BaggageManager.class).createBaggage(COUNTRY_CODE)
.set(span.context(), "FO")) {
assertThat(MDC.get("traceId")).isEqualTo(span.context().traceId());
assertThat(MDC.get(COUNTRY_CODE)).isEqualTo("FO");
try (Tracer.SpanInScope scope2 = tracer.withSpan(null)) {
assertThatMdcContainsUnsetTraceId();
assertThat(MDC.get(COUNTRY_CODE)).isNullOrEmpty();
}
assertThat(MDC.get("traceId")).isEqualTo(span.context().traceId());
assertThat(MDC.get(COUNTRY_CODE)).isEqualTo("FO");
}
finally {
span.end();
}
assertThatMdcContainsUnsetTraceId();
assertThat(MDC.get(COUNTRY_CODE)).isNullOrEmpty();
});
}
private Span createSpan(Tracer tracer) {
return tracer.nextSpan().name("span");
}
private Tracer tracer(ApplicationContext context) {
return context.getBean(Tracer.class);
}
private void assertThatTracingContextIsInitialized(AutoConfig autoConfig) {
if (autoConfig == AutoConfig.OTEL_B3) {
assertThat(Context.current()).isEqualTo(Context.root());
}
}
private void assertThatMdcContainsUnsetTraceId() {
assertThat(isInvalidBraveTraceId() || isInvalidOtelTraceId()).isTrue();
}
private boolean isInvalidBraveTraceId() {
return MDC.get("traceId") == null;
}
private boolean isInvalidOtelTraceId() {
return MDC.get("traceId").equals("00000000000000000000000000000000");
}
enum AutoConfig implements Supplier<ApplicationContextRunner> {
BRAVE_W3C {
@Override
public ApplicationContextRunner get() {
return new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(BraveAutoConfiguration.class)).withPropertyValues(
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
"management.tracing.baggage.correlation.fields=country-code,bp");
}
},
OTEL_W3C {
@Override
public ApplicationContextRunner get() {
return new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
.withPropertyValues(
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
"management.tracing.baggage.correlation.fields=country-code,bp");
}
},
BRAVE_B3 {
@Override
public ApplicationContextRunner get() {
return new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(BraveAutoConfiguration.class))
.withPropertyValues("management.tracing.propagation.type=B3",
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
"management.tracing.baggage.correlation.fields=country-code,bp");
}
},
OTEL_B3 {
@Override
public ApplicationContextRunner get() {
return new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
.withPropertyValues("management.tracing.propagation.type=B3",
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
"management.tracing.baggage.correlation.fields=country-code,bp");
}
}
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.tracing;
import brave.Tracer;
import brave.Tracing;
import brave.baggage.BaggagePropagation;
import brave.http.HttpClientHandler;
import brave.http.HttpClientRequest;
import brave.http.HttpClientResponse;
@@ -26,6 +27,7 @@ import brave.http.HttpServerRequest;
import brave.http.HttpServerResponse;
import brave.http.HttpTracing;
import brave.propagation.CurrentTraceContext;
import brave.propagation.Propagation;
import brave.propagation.Propagation.Factory;
import brave.sampler.Sampler;
import io.micrometer.tracing.brave.bridge.BraveBaggageManager;
@@ -66,9 +68,10 @@ class BraveAutoConfigurationTests {
assertThat(context).hasSingleBean(HttpServerHandler.class);
assertThat(context).hasSingleBean(HttpClientHandler.class);
assertThat(context).hasSingleBean(BraveTracer.class);
assertThat(context).hasSingleBean(BraveBaggageManager.class);
assertThat(context).hasSingleBean(BraveHttpServerHandler.class);
assertThat(context).hasSingleBean(BraveHttpClientHandler.class);
assertThat(context).hasSingleBean(Propagation.Factory.class);
assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class);
});
}
@@ -99,10 +102,22 @@ class BraveAutoConfigurationTests {
assertThat(context).hasSingleBean(BraveHttpServerHandler.class);
assertThat(context).hasBean("customBraveHttpClientHandler");
assertThat(context).hasSingleBean(BraveHttpClientHandler.class);
assertThat(context).hasBean("customHttpServerHandler");
assertThat(context).hasSingleBean(HttpServerHandler.class);
assertThat(context).hasBean("customHttpClientHandler");
assertThat(context).hasSingleBean(HttpClientHandler.class);
});
}
@Test
void shouldSupplyMicrometerBeans() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(BraveTracer.class);
assertThat(context).hasSingleBean(BraveHttpServerHandler.class);
assertThat(context).hasSingleBean(BraveHttpClientHandler.class);
});
}
void shouldNotSupplyBeansIfBraveIsMissing() {
this.contextRunner.withClassLoader(new FilteredClassLoader("brave"))
.run((context) -> assertThat(context).doesNotHaveBean(BraveAutoConfiguration.class));
@@ -114,12 +129,68 @@ class BraveAutoConfigurationTests {
.run((context) -> assertThat(context).doesNotHaveBean(BraveAutoConfiguration.class));
}
@Test
void shouldSupplyW3CPropagationFactoryByDefault() {
this.contextRunner.run((context) -> {
assertThat(context).hasBean("w3cPropagationFactory");
assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class);
});
}
@Test
void shouldSupplyB3PropagationFactoryViaProperty() {
this.contextRunner.withPropertyValues("management.tracing.propagation.type=B3").run((context) -> {
assertThat(context).hasBean("b3PropagationFactory");
assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class);
});
}
@Test
void shouldNotSupplyBeansIfTracingIsDisabled() {
this.contextRunner.withPropertyValues("management.tracing.enabled=false")
.run((context) -> assertThat(context).doesNotHaveBean(BraveAutoConfiguration.class));
}
@Test
void shouldNotSupplyMdcCorrelationScopeWhenMdcNotOnClasspath() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.slf4j")).run((context) -> {
assertThat(context).doesNotHaveBean("mdcCorrelationScopeDecoratorBuilder");
assertThat(context).doesNotHaveBean("correlationScopeDecorator");
});
}
@Test
void shouldNotSupplyCorrelationScopeDecoratorIfBaggageDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false")
.run((context) -> assertThat(context).doesNotHaveBean("correlationScopeDecorator"));
}
@Test
void shouldSupplyW3CWithoutBaggageByDefaultIfBaggageDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false")
.run((context) -> assertThat(context).hasBean("w3cPropagationNoBaggageFactory"));
}
@Test
void shouldSupplyB3WithoutBaggageIfBaggageDisabledAndB3Picked() {
this.contextRunner
.withPropertyValues("management.tracing.baggage.enabled=false",
"management.tracing.propagation.type=B3")
.run((context) -> assertThat(context).hasBean("b3PropagationNoBaggageFactory"));
}
@Test
void shouldNotSupplyCorrelationScopeDecoratorIfBaggageCorrelationDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=false")
.run((context) -> assertThat(context).doesNotHaveBean("correlationFieldsCorrelationScopeDecorator"));
}
@Test
void shouldSupplyMdcCorrelationScopeDecoratorIfBaggageCorrelationDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=false")
.run((context) -> assertThat(context).hasBean("mdcCorrelationScopeDecoratorBuilder"));
}
@Configuration(proxyBeanMethods = false)
private static class CustomConfiguration {

View File

@@ -21,15 +21,21 @@ import io.micrometer.tracing.otel.bridge.OtelHttpClientHandler;
import io.micrometer.tracing.otel.bridge.OtelHttpServerHandler;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Answers;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
@@ -63,7 +69,6 @@ class OpenTelemetryAutoConfigurationTests {
assertThat(context).hasSingleBean(SdkTracerProvider.class);
assertThat(context).hasSingleBean(ContextPropagators.class);
assertThat(context).hasSingleBean(Sampler.class);
assertThat(context).hasSingleBean(SpanProcessor.class);
assertThat(context).hasSingleBean(Tracer.class);
});
}
@@ -108,12 +113,73 @@ class OpenTelemetryAutoConfigurationTests {
assertThat(context).hasBean("customSampler");
assertThat(context).hasSingleBean(Sampler.class);
assertThat(context).hasBean("customSpanProcessor");
assertThat(context).hasSingleBean(SpanProcessor.class);
assertThat(context).hasBean("customTracer");
assertThat(context).hasSingleBean(Tracer.class);
});
}
@Test
void shouldSupplyBaggageAndSlf4jEventListenersWhenMdcOnClasspath() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(Slf4JEventListener.class);
assertThat(context).hasSingleBean(Slf4JBaggageEventListener.class);
});
}
@Test
void shouldSupplySlf4jEventListenersWhenMdcOnClasspathAndBaggageCorrelationDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.correlation.enabled=false").run((context) -> {
assertThat(context).hasSingleBean(Slf4JEventListener.class);
assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class);
});
}
@Test
void shouldSupplySlf4jEventListenersWhenMdcOnClasspathAndBaggageDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false").run((context) -> {
assertThat(context).hasSingleBean(Slf4JEventListener.class);
assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class);
});
}
@Test
void shouldNotSupplySlf4jEventListenersWhenMdcNotOnClasspath() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.slf4j")).run((context) -> {
assertThat(context).doesNotHaveBean(Slf4JEventListener.class);
assertThat(context).doesNotHaveBean(Slf4JBaggageEventListener.class);
});
}
@Test
void shouldSupplyB3PropagationIfPropagationPropertySet() {
this.contextRunner.withPropertyValues("management.tracing.propagation.type=B3").run((context) -> {
assertThat(context).hasSingleBean(B3Propagator.class);
assertThat(context).hasBean("b3TextMapPropagator");
assertThat(context).doesNotHaveBean(W3CTraceContextPropagator.class);
});
}
@Test
void shouldSupplyW3CPropagationWithBaggageByDefault() {
this.contextRunner.run((context) -> assertThat(context).hasBean("w3cTextMapPropagatorWithBaggage"));
}
@Test
void shouldSupplyW3CPropagationWithoutBaggageWhenDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false")
.run((context) -> assertThat(context).hasBean("w3cTextMapPropagatorWithoutBaggage"));
}
@Test
void shouldSupplyB3PropagationWithoutBaggageWhenBaggageDisabledAndB3PropagationEnabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false",
"management.tracing.propagation.type=B3").run((context) -> {
assertThat(context).hasBean("b3TextMapPropagator");
assertThat(context).hasSingleBean(B3Propagator.class);
assertThat(context).doesNotHaveBean("w3cTextMapPropagatorWithoutBaggage");
});
}
@Configuration(proxyBeanMethods = false)
private static class CustomConfiguration {
@@ -174,4 +240,34 @@ class OpenTelemetryAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
private static class OpenTelemetryConfiguration {
@Bean
OpenTelemetry openTelemetry() {
return mock(OpenTelemetry.class, Answers.RETURNS_MOCKS);
}
}
@Configuration(proxyBeanMethods = false)
private static class ContextPropagatorsConfiguration {
@Bean
ContextPropagators contextPropagators() {
return mock(ContextPropagators.class, Answers.RETURNS_MOCKS);
}
}
@Configuration(proxyBeanMethods = false)
private static class CustomFactoryConfiguration {
@Bean
TextMapPropagator customPropagationFactory() {
return mock(TextMapPropagator.class);
}
}
}