Skip to content

Commit 348b8ef

Browse files
authored
Add a SpanProcessor for applying tags from baggage. (#33)
1 parent 58b5e4b commit 348b8ef

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelPropagationConfiguration.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3434
import org.springframework.cloud.sleuth.BaggageManager;
3535
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
36+
import org.springframework.cloud.sleuth.otel.bridge.BaggageTaggingSpanProcessor;
3637
import org.springframework.cloud.sleuth.otel.propagation.BaggageTextMapPropagator;
3738
import org.springframework.cloud.sleuth.otel.propagation.CompositeTextMapPropagator;
3839
import org.springframework.cloud.sleuth.otel.propagation.PropagationType;
@@ -109,6 +110,17 @@ TextMapPropagator baggageTextMapPropagator(SleuthBaggageProperties properties, B
109110

110111
}
111112

113+
@Configuration(proxyBeanMethods = false)
114+
@ConditionalOnProperty(name = "spring.sleuth.baggage.tag-fields")
115+
static class BaggageTaggingConfiguration {
116+
117+
@Bean
118+
BaggageTaggingSpanProcessor baggageTaggingSpanProcessor(SleuthBaggageProperties properties) {
119+
return new BaggageTaggingSpanProcessor(properties.getTagFields());
120+
}
121+
122+
}
123+
112124
static class B3PresentOrPropertyEnabledCondition implements Condition {
113125

114126
@Override
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2013-2020 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.cloud.sleuth.otel.bridge;
18+
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import io.opentelemetry.api.baggage.Baggage;
23+
import io.opentelemetry.api.common.AttributeKey;
24+
import io.opentelemetry.context.Context;
25+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
26+
import io.opentelemetry.sdk.trace.ReadableSpan;
27+
import io.opentelemetry.sdk.trace.SpanProcessor;
28+
29+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
30+
import static java.util.stream.Collectors.toMap;
31+
32+
public class BaggageTaggingSpanProcessor implements SpanProcessor {
33+
34+
private final Map<String, AttributeKey<String>> tagsToApply;
35+
36+
public BaggageTaggingSpanProcessor(List<String> tagsToApply) {
37+
this.tagsToApply = tagsToApply.stream().map(tag -> stringKey(tag))
38+
.collect(toMap(AttributeKey::getKey, key -> key));
39+
}
40+
41+
@Override
42+
public void onStart(Context context, ReadWriteSpan readWriteSpan) {
43+
Baggage baggage = Baggage.fromContext(context);
44+
45+
baggage.forEach((key, baggageEntry) -> {
46+
AttributeKey<String> attributeKey = tagsToApply.get(key);
47+
if (attributeKey != null) {
48+
readWriteSpan.setAttribute(attributeKey, baggageEntry.getValue());
49+
}
50+
});
51+
}
52+
53+
@Override
54+
public boolean isStartRequired() {
55+
return true;
56+
}
57+
58+
@Override
59+
public void onEnd(ReadableSpan readableSpan) {
60+
// no-op
61+
}
62+
63+
@Override
64+
public boolean isEndRequired() {
65+
return false;
66+
}
67+
68+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2013-2020 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.cloud.sleuth.otel.bridge;
18+
19+
import java.util.Arrays;
20+
import java.util.Collections;
21+
22+
import io.opentelemetry.api.baggage.Baggage;
23+
import io.opentelemetry.context.Context;
24+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
25+
import org.junit.jupiter.api.Test;
26+
27+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.mockito.Mockito.mock;
30+
import static org.mockito.Mockito.verify;
31+
import static org.mockito.Mockito.verifyNoInteractions;
32+
import static org.mockito.Mockito.verifyNoMoreInteractions;
33+
34+
class BaggageTaggingSpanProcessorTest {
35+
36+
@Test
37+
void interfaceMethods() {
38+
BaggageTaggingSpanProcessor spanProcessor = new BaggageTaggingSpanProcessor(Collections.emptyList());
39+
assertThat(spanProcessor.isEndRequired()).isFalse();
40+
assertThat(spanProcessor.isStartRequired()).isTrue();
41+
}
42+
43+
@Test
44+
void onStart_emptyBaggage() {
45+
BaggageTaggingSpanProcessor spanProcessor = new BaggageTaggingSpanProcessor(Arrays.asList("tagOne", "tagTwo"));
46+
47+
Baggage baggage = Baggage.builder().build();
48+
ReadWriteSpan span = mock(ReadWriteSpan.class);
49+
50+
spanProcessor.onStart(Context.root().with(baggage), span);
51+
verifyNoInteractions(span);
52+
}
53+
54+
@Test
55+
void onStart_withBaggage() {
56+
BaggageTaggingSpanProcessor spanProcessor = new BaggageTaggingSpanProcessor(Arrays.asList("tagOne", "tagTwo"));
57+
58+
Baggage baggage = Baggage.builder().put("tagOne", "valueOne").put("tagTwo", "valueTwo")
59+
.put("otherTag", "otherValue").build();
60+
ReadWriteSpan span = mock(ReadWriteSpan.class);
61+
62+
spanProcessor.onStart(Context.root().with(baggage), span);
63+
verify(span).setAttribute(stringKey("tagOne"), "valueOne");
64+
verify(span).setAttribute(stringKey("tagTwo"), "valueTwo");
65+
66+
verifyNoMoreInteractions(span);
67+
}
68+
69+
}

0 commit comments

Comments
 (0)