Skip to content

Commit 72d7976

Browse files
Add baggage span processor component (#1290)
1 parent 71e5417 commit 72d7976

File tree

7 files changed

+193
-0
lines changed

7 files changed

+193
-0
lines changed

.github/component_owners.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ components:
1919
aws-xray-propagator:
2020
- wangzlei
2121
- srprash
22+
baggage-procesor:
23+
- mikegoldsmith
2224
compressors:
2325
- jack-berg
2426
consistent-sampling:

baggage-processor/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# OpenTelemetry Baggage Span Processor
2+
3+
The BaggageSpanProcessor reads entries stored in Baggage from the parent context
4+
and adds the baggage keys and values to the `Span` as attributes on start.
5+
6+
Add this span processor to a tracer provider.
7+
8+
Warning!
9+
10+
To repeat: a consequence of adding data to Baggage is that the keys and values
11+
will appear in all outgoing trace context headers from the application.
12+
13+
Do not put sensitive information in Baggage.
14+
15+
## Usage
16+
17+
Add the span processor when configuring the tracer provider.
18+
19+
To configure the span processor to copy all baggage entries during configuration:
20+
21+
```java
22+
import io.opentelemetry.contrib.baggage.processor;
23+
// ...
24+
25+
Tracer tracer = SdkTracerProvider.builder()
26+
.addSpanProcessor(new BaggageSpanProcessor(BaggageSpanProcessor.allowAllBaggageKeys))
27+
.build()
28+
```
29+
30+
Alternatively, you can provide a custom baggage key predicate to select which baggage keys you want to copy.
31+
32+
For example, to only copy baggage entries that start with `my-key`:
33+
34+
```java
35+
new BaggageSpanProcessor(baggageKey -> baggageKey.startsWith("my-key"))
36+
```
37+
38+
For example, to only copy baggage entries that match the regex `^key.+`:
39+
40+
```java
41+
Pattern pattern = Pattern.compile("^key.+");
42+
new BaggageSpanProcessor(baggageKey -> pattern.matcher(baggageKey).matches())
43+
```
44+
45+
## Component owners
46+
47+
- [Mike Golsmith](https://github.com/MikeGoldsmith), Honeycomb
48+
49+
Learn more about component owners in [component_owners.yml](../.github/component_owners.yml).

baggage-processor/build.gradle.kts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
4+
id("otel.publish-conventions")
5+
}
6+
7+
description = "OpenTelemetry Baggage Span Processor"
8+
otelJava.moduleName.set("io.opentelemetry.contrib.baggage.processor")
9+
10+
dependencies {
11+
api("io.opentelemetry:opentelemetry-api")
12+
api("io.opentelemetry:opentelemetry-sdk")
13+
14+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
15+
}

baggage-processor/gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# TODO: uncomment when ready to mark as stable
2+
# otel.stable=true
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.baggage.processor;
7+
8+
import io.opentelemetry.api.baggage.Baggage;
9+
import io.opentelemetry.context.Context;
10+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
11+
import io.opentelemetry.sdk.trace.ReadableSpan;
12+
import io.opentelemetry.sdk.trace.SpanProcessor;
13+
import java.util.function.Predicate;
14+
15+
/**
16+
* This span processor copies attributes stored in {@link Baggage} into each newly created {@link
17+
* io.opentelemetry.api.trace.Span}.
18+
*/
19+
public class BaggageSpanProcessor implements SpanProcessor {
20+
private final Predicate<String> baggageKeyPredicate;
21+
22+
/** A {@link Predicate} that returns true for all baggage keys. */
23+
public static final Predicate<String> allowAllBaggageKeys = baggageKey -> true;
24+
25+
/**
26+
* Creates a new {@link BaggageSpanProcessor} that copies only baggage entries with keys that pass
27+
* the provided filter into the newly created {@link io.opentelemetry.api.trace.Span}.
28+
*/
29+
public BaggageSpanProcessor(Predicate<String> baggageKeyPredicate) {
30+
this.baggageKeyPredicate = baggageKeyPredicate;
31+
}
32+
33+
@Override
34+
public void onStart(Context parentContext, ReadWriteSpan span) {
35+
Baggage.fromContext(parentContext)
36+
.forEach(
37+
(s, baggageEntry) -> {
38+
if (baggageKeyPredicate.test(s)) {
39+
span.setAttribute(s, baggageEntry.getValue());
40+
}
41+
});
42+
}
43+
44+
@Override
45+
public boolean isStartRequired() {
46+
return true;
47+
}
48+
49+
@Override
50+
public void onEnd(ReadableSpan span) {}
51+
52+
@Override
53+
public boolean isEndRequired() {
54+
return false;
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.baggage.processor;
7+
8+
import io.opentelemetry.api.baggage.Baggage;
9+
import io.opentelemetry.context.Context;
10+
import io.opentelemetry.context.Scope;
11+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
12+
import java.util.regex.Pattern;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.extension.ExtendWith;
15+
import org.mockito.Mock;
16+
import org.mockito.Mockito;
17+
import org.mockito.junit.jupiter.MockitoExtension;
18+
19+
@ExtendWith(MockitoExtension.class)
20+
public class BaggageSpanProcessorTest {
21+
22+
@Test
23+
public void test_baggageSpanProcessor_adds_attributes_to_spans(@Mock ReadWriteSpan span) {
24+
try (BaggageSpanProcessor processor =
25+
new BaggageSpanProcessor(BaggageSpanProcessor.allowAllBaggageKeys)) {
26+
try (Scope ignore = Baggage.current().toBuilder().put("key", "value").build().makeCurrent()) {
27+
processor.onStart(Context.current(), span);
28+
Mockito.verify(span).setAttribute("key", "value");
29+
}
30+
}
31+
}
32+
33+
@Test
34+
public void test_baggageSpanProcessor_adds_attributes_to_spans_when_key_filter_matches(
35+
@Mock ReadWriteSpan span) {
36+
try (BaggageSpanProcessor processor = new BaggageSpanProcessor(key -> key.startsWith("k"))) {
37+
try (Scope ignore =
38+
Baggage.current().toBuilder()
39+
.put("key", "value")
40+
.put("other", "value")
41+
.build()
42+
.makeCurrent()) {
43+
processor.onStart(Context.current(), span);
44+
Mockito.verify(span).setAttribute("key", "value");
45+
Mockito.verify(span, Mockito.never()).setAttribute("other", "value");
46+
}
47+
}
48+
}
49+
50+
@Test
51+
public void test_baggageSpanProcessor_adds_attributes_to_spans_when_key_filter_matches_regex(
52+
@Mock ReadWriteSpan span) {
53+
Pattern pattern = Pattern.compile("k.*");
54+
try (BaggageSpanProcessor processor =
55+
new BaggageSpanProcessor(key -> pattern.matcher(key).matches())) {
56+
try (Scope ignore =
57+
Baggage.current().toBuilder()
58+
.put("key", "value")
59+
.put("other", "value")
60+
.build()
61+
.makeCurrent()) {
62+
processor.onStart(Context.current(), span);
63+
Mockito.verify(span).setAttribute("key", "value");
64+
Mockito.verify(span, Mockito.never()).setAttribute("other", "value");
65+
}
66+
}
67+
}
68+
}

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ include(":all")
6262
include(":aws-resources")
6363
include(":aws-xray")
6464
include(":aws-xray-propagator")
65+
include(":baggage-processor")
6566
include(":compressors:compressor-zstd")
6667
include(":consistent-sampling")
6768
include(":dependencyManagement")

0 commit comments

Comments
 (0)