Skip to content

Commit 3acd9b8

Browse files
committed
Merge pull request #32480 from marcingrzejszczak
* gh-32480: Polish "Add support for MDC, Context Propagation (via B3 and W3C), and Baggage" Add support for MDC, Context Propagation (via B3 and W3C), and Baggage Closes gh-32480
2 parents bf5bd4f + 36a4b36 commit 3acd9b8

File tree

7 files changed

+780
-34
lines changed

7 files changed

+780
-34
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/BraveAutoConfiguration.java

+110-11
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222
import brave.Tracing;
2323
import brave.Tracing.Builder;
2424
import brave.TracingCustomizer;
25+
import brave.baggage.BaggageField;
26+
import brave.baggage.BaggagePropagation;
27+
import brave.baggage.BaggagePropagation.FactoryBuilder;
28+
import brave.baggage.BaggagePropagationConfig;
29+
import brave.baggage.BaggagePropagationCustomizer;
30+
import brave.baggage.CorrelationScopeConfig;
31+
import brave.baggage.CorrelationScopeCustomizer;
32+
import brave.baggage.CorrelationScopeDecorator;
33+
import brave.context.slf4j.MDCScopeDecorator;
2534
import brave.handler.SpanHandler;
2635
import brave.http.HttpClientHandler;
2736
import brave.http.HttpClientRequest;
@@ -41,20 +50,27 @@
4150
import io.micrometer.tracing.brave.bridge.BraveCurrentTraceContext;
4251
import io.micrometer.tracing.brave.bridge.BraveHttpClientHandler;
4352
import io.micrometer.tracing.brave.bridge.BraveHttpServerHandler;
53+
import io.micrometer.tracing.brave.bridge.BravePropagator;
4454
import io.micrometer.tracing.brave.bridge.BraveTracer;
55+
import io.micrometer.tracing.brave.bridge.W3CPropagation;
4556

57+
import org.springframework.beans.factory.ObjectProvider;
4658
import org.springframework.boot.autoconfigure.AutoConfiguration;
4759
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
4860
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
4961
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
62+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
5063
import org.springframework.boot.context.properties.EnableConfigurationProperties;
5164
import org.springframework.context.annotation.Bean;
65+
import org.springframework.context.annotation.Configuration;
66+
import org.springframework.core.annotation.Order;
5267
import org.springframework.core.env.Environment;
5368

5469
/**
5570
* {@link EnableAutoConfiguration Auto-configuration} for Brave.
5671
*
5772
* @author Moritz Halbritter
73+
* @author Marcin Grzejszczak
5874
* @since 3.0.0
5975
*/
6076
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@@ -63,6 +79,8 @@
6379
@ConditionalOnEnabledTracing
6480
public class BraveAutoConfiguration {
6581

82+
private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager();
83+
6684
/**
6785
* Default value for application name if {@code spring.application.name} is not set.
6886
*/
@@ -105,12 +123,6 @@ public CurrentTraceContext braveCurrentTraceContext(List<CurrentTraceContext.Sco
105123
return builder.build();
106124
}
107125

108-
@Bean
109-
@ConditionalOnMissingBean
110-
public Factory bravePropagationFactory() {
111-
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
112-
}
113-
114126
@Bean
115127
@ConditionalOnMissingBean
116128
public Sampler braveSampler(TracingProperties properties) {
@@ -137,15 +149,14 @@ public HttpClientHandler<HttpClientRequest, HttpClientResponse> httpClientHandle
137149

138150
@Bean
139151
@ConditionalOnMissingBean
140-
BraveTracer braveTracerBridge(brave.Tracer tracer, CurrentTraceContext currentTraceContext,
141-
BraveBaggageManager braveBaggageManager) {
142-
return new BraveTracer(tracer, new BraveCurrentTraceContext(currentTraceContext), braveBaggageManager);
152+
BraveTracer braveTracerBridge(brave.Tracer tracer, CurrentTraceContext currentTraceContext) {
153+
return new BraveTracer(tracer, new BraveCurrentTraceContext(currentTraceContext), BRAVE_BAGGAGE_MANAGER);
143154
}
144155

145156
@Bean
146157
@ConditionalOnMissingBean
147-
BraveBaggageManager braveBaggageManager() {
148-
return new BraveBaggageManager();
158+
BravePropagator bravePropagator(Tracing tracing) {
159+
return new BravePropagator(tracing);
149160
}
150161

151162
@Bean
@@ -162,4 +173,92 @@ BraveHttpClientHandler braveHttpClientHandler(
162173
return new BraveHttpClientHandler(httpClientHandler);
163174
}
164175

176+
@Configuration(proxyBeanMethods = false)
177+
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false")
178+
static class BraveNoBaggageConfiguration {
179+
180+
@Bean
181+
@ConditionalOnMissingBean
182+
Factory propagationFactory(TracingProperties tracing) {
183+
return switch (tracing.getPropagation().getType()) {
184+
case B3 ->
185+
B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
186+
case W3C -> new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of());
187+
};
188+
}
189+
190+
}
191+
192+
@Configuration(proxyBeanMethods = false)
193+
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true)
194+
static class BraveBaggageConfiguration {
195+
196+
private final TracingProperties tracingProperties;
197+
198+
BraveBaggageConfiguration(TracingProperties tracingProperties) {
199+
this.tracingProperties = tracingProperties;
200+
}
201+
202+
@Bean
203+
@ConditionalOnMissingBean
204+
BaggagePropagation.FactoryBuilder propagationFactoryBuilder(
205+
ObjectProvider<BaggagePropagationCustomizer> baggagePropagationCustomizers) {
206+
Factory delegate = switch (this.tracingProperties.getPropagation().getType()) {
207+
case B3 ->
208+
B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
209+
case W3C -> new W3CPropagation(BRAVE_BAGGAGE_MANAGER, List.of());
210+
};
211+
FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(delegate);
212+
baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
213+
return builder;
214+
}
215+
216+
@Bean
217+
@Order(0)
218+
BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer() {
219+
return (builder) -> {
220+
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
221+
for (String fieldName : remoteFields) {
222+
builder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName)));
223+
}
224+
};
225+
}
226+
227+
@Bean
228+
@ConditionalOnMissingBean
229+
Factory propagationFactory(BaggagePropagation.FactoryBuilder factoryBuilder) {
230+
return factoryBuilder.build();
231+
}
232+
233+
@Bean
234+
@ConditionalOnMissingBean
235+
CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder(
236+
ObjectProvider<CorrelationScopeCustomizer> correlationScopeCustomizers) {
237+
CorrelationScopeDecorator.Builder builder = MDCScopeDecorator.newBuilder();
238+
correlationScopeCustomizers.forEach((customizer) -> customizer.customize(builder));
239+
return builder;
240+
}
241+
242+
@Bean
243+
@Order(0)
244+
@ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled",
245+
matchIfMissing = true)
246+
CorrelationScopeCustomizer correlationFieldsCorrelationScopeCustomizer() {
247+
return (builder) -> {
248+
List<String> correlationFields = this.tracingProperties.getBaggage().getCorrelation().getFields();
249+
for (String field : correlationFields) {
250+
builder.add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(BaggageField.create(field))
251+
.flushOnUpdate().build());
252+
}
253+
};
254+
}
255+
256+
@Bean
257+
@ConditionalOnMissingBean(CorrelationScopeDecorator.class)
258+
ScopeDecorator correlationScopeDecorator(CorrelationScopeDecorator.Builder builder) {
259+
return builder.build();
260+
}
261+
262+
}
263+
165264
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java

+120-17
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,27 @@
2323
import io.micrometer.tracing.SamplerFunction;
2424
import io.micrometer.tracing.otel.bridge.DefaultHttpClientAttributesGetter;
2525
import io.micrometer.tracing.otel.bridge.DefaultHttpServerAttributesExtractor;
26+
import io.micrometer.tracing.otel.bridge.EventListener;
27+
import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper;
2628
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
2729
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
2830
import io.micrometer.tracing.otel.bridge.OtelHttpClientHandler;
2931
import io.micrometer.tracing.otel.bridge.OtelHttpServerHandler;
32+
import io.micrometer.tracing.otel.bridge.OtelPropagator;
3033
import io.micrometer.tracing.otel.bridge.OtelTracer;
3134
import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
35+
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
36+
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
37+
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
3238
import io.opentelemetry.api.OpenTelemetry;
39+
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
3340
import io.opentelemetry.api.common.Attributes;
3441
import io.opentelemetry.api.trace.Tracer;
42+
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
43+
import io.opentelemetry.context.ContextStorage;
3544
import io.opentelemetry.context.propagation.ContextPropagators;
3645
import io.opentelemetry.context.propagation.TextMapPropagator;
46+
import io.opentelemetry.extension.trace.propagation.B3Propagator;
3747
import io.opentelemetry.sdk.OpenTelemetrySdk;
3848
import io.opentelemetry.sdk.resources.Resource;
3949
import io.opentelemetry.sdk.trace.SdkTracerProvider;
@@ -44,13 +54,16 @@
4454
import io.opentelemetry.sdk.trace.samplers.Sampler;
4555
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
4656

57+
import org.springframework.beans.factory.ObjectProvider;
4758
import org.springframework.boot.SpringBootVersion;
4859
import org.springframework.boot.autoconfigure.AutoConfiguration;
4960
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5061
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
5162
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
63+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
5264
import org.springframework.boot.context.properties.EnableConfigurationProperties;
5365
import org.springframework.context.annotation.Bean;
66+
import org.springframework.context.annotation.Configuration;
5467
import org.springframework.core.env.Environment;
5568

5669
/**
@@ -70,6 +83,12 @@ public class OpenTelemetryAutoConfiguration {
7083
*/
7184
private static final String DEFAULT_APPLICATION_NAME = "application";
7285

86+
private final TracingProperties tracingProperties;
87+
88+
OpenTelemetryAutoConfiguration(TracingProperties tracingProperties) {
89+
this.tracingProperties = tracingProperties;
90+
}
91+
7392
@Bean
7493
@ConditionalOnMissingBean
7594
OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) {
@@ -79,33 +98,31 @@ OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagat
7998

8099
@Bean
81100
@ConditionalOnMissingBean
82-
SdkTracerProvider otelSdkTracerProvider(Environment environment, List<SpanProcessor> spanProcessors,
101+
SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider<SpanProcessor> spanProcessors,
83102
Sampler sampler) {
84103
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
85104
SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler)
86105
.setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
87-
for (SpanProcessor spanProcessor : spanProcessors) {
88-
builder.addSpanProcessor(spanProcessor);
89-
}
106+
spanProcessors.orderedStream().forEach(builder::addSpanProcessor);
90107
return builder.build();
91108
}
92109

93110
@Bean
94111
@ConditionalOnMissingBean
95-
ContextPropagators otelContextPropagators(List<TextMapPropagator> textMapPropagators) {
96-
return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators));
112+
ContextPropagators otelContextPropagators(ObjectProvider<TextMapPropagator> textMapPropagators) {
113+
return ContextPropagators
114+
.create(TextMapPropagator.composite(textMapPropagators.orderedStream().collect(Collectors.toList())));
97115
}
98116

99117
@Bean
100118
@ConditionalOnMissingBean
101-
Sampler otelSampler(TracingProperties properties) {
102-
return Sampler.traceIdRatioBased(properties.getSampling().getProbability());
119+
Sampler otelSampler() {
120+
return Sampler.traceIdRatioBased(this.tracingProperties.getSampling().getProbability());
103121
}
104122

105123
@Bean
106-
@ConditionalOnMissingBean
107-
SpanProcessor otelSpanProcessor(List<SpanExporter> spanExporter) {
108-
return SpanProcessor.composite(spanExporter.stream()
124+
SpanProcessor otelSpanProcessor(ObjectProvider<SpanExporter> spanExporters) {
125+
return SpanProcessor.composite(spanExporters.orderedStream()
109126
.map((exporter) -> BatchSpanProcessor.builder(exporter).build()).collect(Collectors.toList()));
110127
}
111128

@@ -119,20 +136,26 @@ Tracer otelTracer(OpenTelemetry openTelemetry) {
119136
@ConditionalOnMissingBean
120137
OtelTracer micrometerOtelTracer(Tracer tracer, EventPublisher eventPublisher,
121138
OtelCurrentTraceContext otelCurrentTraceContext) {
122-
return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher,
123-
new OtelBaggageManager(otelCurrentTraceContext, List.of(), List.of()));
139+
return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher, new OtelBaggageManager(
140+
otelCurrentTraceContext, this.tracingProperties.getBaggage().getRemoteFields(), List.of()));
124141
}
125142

126143
@Bean
127144
@ConditionalOnMissingBean
128-
EventPublisher otelTracerEventPublisher() {
129-
return (event) -> {
130-
};
145+
OtelPropagator otelPropagator(ContextPropagators contextPropagators, Tracer tracer) {
146+
return new OtelPropagator(contextPropagators, tracer);
131147
}
132148

133149
@Bean
134150
@ConditionalOnMissingBean
135-
OtelCurrentTraceContext otelCurrentTraceContext() {
151+
EventPublisher otelTracerEventPublisher(List<EventListener> eventListeners) {
152+
return new OTelEventPublisher(eventListeners);
153+
}
154+
155+
@Bean
156+
@ConditionalOnMissingBean
157+
OtelCurrentTraceContext otelCurrentTraceContext(EventPublisher publisher) {
158+
ContextStorage.addWrapper(new EventPublishingContextWrapper(publisher));
136159
return new OtelCurrentTraceContext();
137160
}
138161

@@ -150,4 +173,84 @@ OtelHttpServerHandler otelHttpServerHandler(OpenTelemetry openTelemetry) {
150173
new DefaultHttpServerAttributesExtractor());
151174
}
152175

176+
@Bean
177+
@ConditionalOnMissingBean
178+
Slf4JEventListener otelSlf4JEventListener() {
179+
return new Slf4JEventListener();
180+
}
181+
182+
@Configuration(proxyBeanMethods = false)
183+
@ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", matchIfMissing = true)
184+
static class BaggageConfiguration {
185+
186+
private final TracingProperties tracingProperties;
187+
188+
BaggageConfiguration(TracingProperties tracingProperties) {
189+
this.tracingProperties = tracingProperties;
190+
}
191+
192+
@Bean
193+
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "W3C",
194+
matchIfMissing = true)
195+
TextMapPropagator w3cTextMapPropagatorWithBaggage() {
196+
return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(),
197+
W3CBaggagePropagator.getInstance());
198+
}
199+
200+
@Bean
201+
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "B3")
202+
TextMapPropagator b3BaggageTextMapPropagator(OtelCurrentTraceContext otelCurrentTraceContext) {
203+
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
204+
return TextMapPropagator.composite(B3Propagator.injectingSingleHeader(), new BaggageTextMapPropagator(
205+
remoteFields, new OtelBaggageManager(otelCurrentTraceContext, remoteFields, List.of())));
206+
}
207+
208+
@Bean
209+
@ConditionalOnMissingBean
210+
@ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled",
211+
matchIfMissing = true)
212+
Slf4JBaggageEventListener otelSlf4JBaggageEventListener() {
213+
return new Slf4JBaggageEventListener(this.tracingProperties.getBaggage().getCorrelation().getFields());
214+
}
215+
216+
}
217+
218+
@Configuration(proxyBeanMethods = false)
219+
@ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", havingValue = "false")
220+
static class NoBaggageConfiguration {
221+
222+
@Bean
223+
@ConditionalOnMissingBean
224+
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "B3")
225+
B3Propagator b3TextMapPropagator() {
226+
return B3Propagator.injectingSingleHeader();
227+
}
228+
229+
@Bean
230+
@ConditionalOnMissingBean
231+
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "W3C",
232+
matchIfMissing = true)
233+
W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() {
234+
return W3CTraceContextPropagator.getInstance();
235+
}
236+
237+
}
238+
239+
static class OTelEventPublisher implements EventPublisher {
240+
241+
private final List<EventListener> listeners;
242+
243+
OTelEventPublisher(List<EventListener> listeners) {
244+
this.listeners = listeners;
245+
}
246+
247+
@Override
248+
public void publishEvent(Object event) {
249+
for (EventListener listener : this.listeners) {
250+
listener.onEvent(event);
251+
}
252+
}
253+
254+
}
255+
153256
}

0 commit comments

Comments
 (0)