Skip to content

Commit 4dd3d42

Browse files
committed
Implement multiple tracing headers when using OpenTelemetry
1 parent fa1f3e8 commit 4dd3d42

File tree

2 files changed

+159
-27
lines changed

2 files changed

+159
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.tracing;
18+
19+
import java.util.Collection;
20+
import java.util.stream.Collectors;
21+
import java.util.stream.Stream;
22+
import java.util.stream.StreamSupport;
23+
24+
import io.opentelemetry.context.Context;
25+
import io.opentelemetry.context.propagation.TextMapGetter;
26+
import io.opentelemetry.context.propagation.TextMapPropagator;
27+
import io.opentelemetry.context.propagation.TextMapSetter;
28+
29+
/**
30+
* {@link TextMapPropagator} which supports multiple tracing formats. It is able to
31+
* configure different formats for injecting and for extracting.
32+
*
33+
* @author Moritz Halbritter
34+
*/
35+
class CompositeTextMapPropagator implements TextMapPropagator {
36+
37+
private final Iterable<TextMapPropagator> injectors;
38+
39+
private final Iterable<TextMapPropagator> extractors;
40+
41+
CompositeTextMapPropagator(Iterable<TextMapPropagator> injectors, Iterable<TextMapPropagator> extractors) {
42+
this.injectors = injectors;
43+
this.extractors = extractors;
44+
}
45+
46+
@Override
47+
public Collection<String> fields() {
48+
return Stream.concat(stream(this.injectors), stream(this.extractors))
49+
.flatMap((entry) -> entry.fields().stream())
50+
.collect(Collectors.toSet());
51+
}
52+
53+
@Override
54+
public <C> void inject(Context context, C carrier, TextMapSetter<C> setter) {
55+
if (context == null || setter == null) {
56+
return;
57+
}
58+
for (TextMapPropagator injector : this.injectors) {
59+
injector.inject(context, carrier, setter);
60+
}
61+
}
62+
63+
@Override
64+
public <C> Context extract(Context context, C carrier, TextMapGetter<C> getter) {
65+
if (context == null) {
66+
return Context.root();
67+
}
68+
if (getter == null) {
69+
return context;
70+
}
71+
for (TextMapPropagator extractor : this.extractors) {
72+
Context extractedContext = extractor.extract(context, carrier, getter);
73+
if (extractedContext != context) {
74+
return extractedContext;
75+
}
76+
}
77+
return context;
78+
}
79+
80+
private static <T> Stream<T> stream(Iterable<T> iterable) {
81+
return StreamSupport.stream(iterable.spliterator(), false);
82+
}
83+
84+
}

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

+75-27
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.tracing;
1818

19+
import java.util.ArrayList;
20+
import java.util.Collection;
1921
import java.util.Collections;
2022
import java.util.List;
2123

@@ -52,6 +54,7 @@
5254

5355
import org.springframework.beans.factory.ObjectProvider;
5456
import org.springframework.boot.SpringBootVersion;
57+
import org.springframework.boot.actuate.autoconfigure.tracing.TracingProperties.Propagation.PropagationType;
5558
import org.springframework.boot.autoconfigure.AutoConfiguration;
5659
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5760
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -66,6 +69,7 @@
6669
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
6770
*
6871
* @author Moritz Halbritter
72+
* @author Marcin Grzejszczak
6973
* @since 3.0.0
7074
*/
7175
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@@ -185,22 +189,17 @@ static class BaggageConfiguration {
185189
}
186190

187191
@Bean
188-
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "W3C",
189-
matchIfMissing = true)
190-
TextMapPropagator w3cTextMapPropagatorWithBaggage(OtelCurrentTraceContext otelCurrentTraceContext) {
192+
TextMapPropagator textMapPropagatorWithBaggage(OtelCurrentTraceContext otelCurrentTraceContext) {
191193
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
192-
return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(),
193-
W3CBaggagePropagator.getInstance(), new BaggageTextMapPropagator(remoteFields,
194-
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList())));
195-
}
196-
197-
@Bean
198-
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "B3")
199-
TextMapPropagator b3BaggageTextMapPropagator(OtelCurrentTraceContext otelCurrentTraceContext) {
200-
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
201-
return TextMapPropagator.composite(B3Propagator.injectingSingleHeader(),
202-
new BaggageTextMapPropagator(remoteFields,
203-
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList())));
194+
BaggageTextMapPropagator baggagePropagator = new BaggageTextMapPropagator(remoteFields,
195+
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()));
196+
List<TextMapPropagator> injectors = new ArrayList<>(
197+
TextMapPropagatorFactory.forTypes(this.tracingProperties.getPropagation().getType(), true));
198+
injectors.add(baggagePropagator);
199+
List<TextMapPropagator> extractors = new ArrayList<>(
200+
TextMapPropagatorFactory.forTypes(PropagationType.orderedValues(), true));
201+
extractors.add(baggagePropagator);
202+
return new CompositeTextMapPropagator(injectors, extractors);
204203
}
205204

206205
@Bean
@@ -218,18 +217,12 @@ Slf4JBaggageEventListener otelSlf4JBaggageEventListener() {
218217
static class NoBaggageConfiguration {
219218

220219
@Bean
221-
@ConditionalOnMissingBean
222-
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "B3")
223-
B3Propagator b3TextMapPropagator() {
224-
return B3Propagator.injectingSingleHeader();
225-
}
226-
227-
@Bean
228-
@ConditionalOnMissingBean
229-
@ConditionalOnProperty(prefix = "management.tracing.propagation", name = "type", havingValue = "W3C",
230-
matchIfMissing = true)
231-
W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() {
232-
return W3CTraceContextPropagator.getInstance();
220+
TextMapPropagator textMapPropagator(TracingProperties properties) {
221+
List<TextMapPropagator> injectors = TextMapPropagatorFactory.forTypes(properties.getPropagation().getType(),
222+
false);
223+
List<TextMapPropagator> extractors = TextMapPropagatorFactory.forTypes(PropagationType.orderedValues(),
224+
false);
225+
return new CompositeTextMapPropagator(injectors, extractors);
233226
}
234227

235228
}
@@ -251,4 +244,59 @@ public void publishEvent(Object event) {
251244

252245
}
253246

247+
/**
248+
* Factory for {@link TextMapPropagator TextMapPropagators}.
249+
*/
250+
private static final class TextMapPropagatorFactory {
251+
252+
private TextMapPropagatorFactory() {
253+
}
254+
255+
/**
256+
* Creates a new B3 propagator using a single B3 header.
257+
* @return the B3 propagator
258+
*/
259+
private static TextMapPropagator b3Single() {
260+
return B3Propagator.injectingSingleHeader();
261+
}
262+
263+
/**
264+
* Creates a new B3 propagator using multiple B3 headers.
265+
* @return the B3 propagator
266+
*/
267+
private static TextMapPropagator b3Multi() {
268+
return B3Propagator.injectingMultiHeaders();
269+
}
270+
271+
/**
272+
* Creates a new W3C propagator.
273+
* @param baggage whether baggage propagation should be supported
274+
* @return the W3C propagator
275+
*/
276+
private static TextMapPropagator w3c(boolean baggage) {
277+
if (!baggage) {
278+
return W3CTraceContextPropagator.getInstance();
279+
}
280+
return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(),
281+
W3CBaggagePropagator.getInstance());
282+
}
283+
284+
private static TextMapPropagator forType(PropagationType type, boolean baggage) {
285+
return switch (type) {
286+
case B3 -> b3Single();
287+
case B3_MULTI -> b3Multi();
288+
case W3C -> w3c(baggage);
289+
};
290+
}
291+
292+
private static List<TextMapPropagator> forTypes(Collection<PropagationType> types, boolean baggage) {
293+
List<TextMapPropagator> result = new ArrayList<>(types.size());
294+
for (PropagationType type : types) {
295+
result.add(forType(type, baggage));
296+
}
297+
return result;
298+
}
299+
300+
}
301+
254302
}

0 commit comments

Comments
 (0)