Skip to content

Commit 59a737c

Browse files
Add key predicate to baggage span processor (open-telemetry#2535)
* Add key predicate to baggage span processor * add changelog entry * fix linting * more linter fixes --------- Co-authored-by: Diego Hurtado <[email protected]>
1 parent 88111d0 commit 59a737c

File tree

5 files changed

+126
-10
lines changed

5 files changed

+126
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397)))
3737
- `opentelemetry-processor-baggage` Initial release
3838
([#2436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2436))
39+
- `opentelemetry-processor-baggage` Add baggage key predicate
40+
([#2535](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2535))
3941

4042
### Fixed
4143

processor/opentelemetry-processor-baggage/README.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,32 @@ Do not put sensitive information in Baggage.
2020

2121
To repeat: a consequence of adding data to Baggage is that the keys and
2222
values will appear in all outgoing HTTP headers from the application.
23+
24+
## Usage
25+
26+
Add the span processor when configuring the tracer provider.
27+
28+
To configure the span processor to copy all baggage entries during configuration:
29+
30+
```python
31+
from opentelemetry.processor.baggage import BaggageSpanProcessor, ALLOW_ALL_BAGGAGE_KEYS
32+
33+
tracer_provider = TracerProvider()
34+
tracer_provider.add_span_processor(BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS))
35+
```
36+
37+
Alternatively, you can provide a custom baggage key predicate to select which baggage keys you want to copy.
38+
39+
For example, to only copy baggage entries that start with `my-key`:
40+
41+
```python
42+
starts_with_predicate = lambda baggage_key: baggage_key.startswith("my-key")
43+
tracer_provider.add_span_processor(BaggageSpanProcessor(starts_with_predicate))
44+
```
45+
46+
For example, to only copy baggage entries that match the regex `^key.+`:
47+
48+
```python
49+
regex_predicate = lambda baggage_key: baggage_key.startswith("^key.+")
50+
tracer_provider.add_span_processor(BaggageSpanProcessor(regex_predicate))
51+
```

processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
# pylint: disable=import-error
1616

17-
from .processor import BaggageSpanProcessor
17+
from .processor import ALLOW_ALL_BAGGAGE_KEYS, BaggageSpanProcessor
1818
from .version import __version__
1919

20-
__all__ = ["BaggageSpanProcessor", "__version__"]
20+
__all__ = ["ALLOW_ALL_BAGGAGE_KEYS", "BaggageSpanProcessor", "__version__"]

processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import Optional
15+
from typing import Callable, Optional
1616

1717
from opentelemetry.baggage import get_all as get_all_baggage
1818
from opentelemetry.context import Context
1919
from opentelemetry.sdk.trace.export import SpanProcessor
2020
from opentelemetry.trace import Span
2121

22+
# A BaggageKeyPredicate is a function that takes a baggage key and returns a boolean
23+
BaggageKeyPredicateT = Callable[[str], bool]
24+
25+
# A BaggageKeyPredicate that always returns True, allowing all baggage keys to be added to spans
26+
ALLOW_ALL_BAGGAGE_KEYS: BaggageKeyPredicateT = lambda _: True
27+
2228

2329
class BaggageSpanProcessor(SpanProcessor):
2430
"""
@@ -44,12 +50,13 @@ class BaggageSpanProcessor(SpanProcessor):
4450
4551
"""
4652

47-
def __init__(self) -> None:
48-
pass
53+
def __init__(self, baggage_key_predicate: BaggageKeyPredicateT) -> None:
54+
self._baggage_key_predicate = baggage_key_predicate
4955

5056
def on_start(
5157
self, span: "Span", parent_context: Optional[Context] = None
5258
) -> None:
5359
baggage = get_all_baggage(parent_context)
5460
for key, value in baggage.items():
55-
span.set_attribute(key, value)
61+
if self._baggage_key_predicate(key):
62+
span.set_attribute(key, value)

processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,94 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import re
1516
import unittest
1617

1718
from opentelemetry.baggage import get_all as get_all_baggage
1819
from opentelemetry.baggage import set_baggage
1920
from opentelemetry.context import attach, detach
20-
from opentelemetry.processor.baggage import BaggageSpanProcessor
21+
from opentelemetry.processor.baggage import (
22+
ALLOW_ALL_BAGGAGE_KEYS,
23+
BaggageSpanProcessor,
24+
)
2125
from opentelemetry.sdk.trace import TracerProvider
2226
from opentelemetry.sdk.trace.export import SpanProcessor
2327
from opentelemetry.trace import Span, Tracer
2428

2529

2630
class BaggageSpanProcessorTest(unittest.TestCase):
2731
def test_check_the_baggage(self):
28-
self.assertIsInstance(BaggageSpanProcessor(), SpanProcessor)
32+
self.assertIsInstance(
33+
BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS), SpanProcessor
34+
)
2935

3036
def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context(
3137
self,
3238
):
3339
tracer_provider = TracerProvider()
34-
tracer_provider.add_span_processor(BaggageSpanProcessor())
40+
tracer_provider.add_span_processor(
41+
BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS)
42+
)
43+
44+
# tracer has no baggage to start
45+
tracer = tracer_provider.get_tracer("my-tracer")
46+
self.assertIsInstance(tracer, Tracer)
47+
self.assertEqual(get_all_baggage(), {})
48+
# set baggage in context
49+
ctx = set_baggage("queen", "bee")
50+
with tracer.start_as_current_span(
51+
name="bumble", context=ctx
52+
) as bumble_span:
53+
# span should have baggage key-value pair in context
54+
self.assertEqual(get_all_baggage(ctx), {"queen": "bee"})
55+
# span should have baggage key-value pair in attribute
56+
self.assertEqual(bumble_span._attributes["queen"], "bee")
57+
with tracer.start_as_current_span(
58+
name="child_span", context=ctx
59+
) as child_span:
60+
self.assertIsInstance(child_span, Span)
61+
# child span should have baggage key-value pair in context
62+
self.assertEqual(get_all_baggage(ctx), {"queen": "bee"})
63+
# child span should have baggage key-value pair in attribute
64+
self.assertEqual(child_span._attributes["queen"], "bee")
65+
66+
def test_baggage_span_processor_with_string_prefix(
67+
self,
68+
):
69+
tracer_provider = TracerProvider()
70+
tracer_provider.add_span_processor(
71+
BaggageSpanProcessor(self.has_prefix)
72+
)
73+
74+
# tracer has no baggage to start
75+
tracer = tracer_provider.get_tracer("my-tracer")
76+
self.assertIsInstance(tracer, Tracer)
77+
self.assertEqual(get_all_baggage(), {})
78+
# set baggage in context
79+
ctx = set_baggage("queen", "bee")
80+
with tracer.start_as_current_span(
81+
name="bumble", context=ctx
82+
) as bumble_span:
83+
# span should have baggage key-value pair in context
84+
self.assertEqual(get_all_baggage(ctx), {"queen": "bee"})
85+
# span should have baggage key-value pair in attribute
86+
self.assertEqual(bumble_span._attributes["queen"], "bee")
87+
with tracer.start_as_current_span(
88+
name="child_span", context=ctx
89+
) as child_span:
90+
self.assertIsInstance(child_span, Span)
91+
# child span should have baggage key-value pair in context
92+
self.assertEqual(get_all_baggage(ctx), {"queen": "bee"})
93+
# child span should have baggage key-value pair in attribute
94+
self.assertEqual(child_span._attributes["queen"], "bee")
95+
96+
def test_baggage_span_processor_with_regex(
97+
self,
98+
):
99+
tracer_provider = TracerProvider()
100+
tracer_provider.add_span_processor(
101+
BaggageSpanProcessor(self.matches_regex)
102+
)
35103

36104
# tracer has no baggage to start
37105
tracer = tracer_provider.get_tracer("my-tracer")
@@ -59,7 +127,9 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(
59127
self,
60128
):
61129
tracer_provider = TracerProvider()
62-
tracer_provider.add_span_processor(BaggageSpanProcessor())
130+
tracer_provider.add_span_processor(
131+
BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS)
132+
)
63133

64134
# tracer has no baggage to start
65135
tracer = tracer_provider.get_tracer("my-tracer")
@@ -87,3 +157,11 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(
87157
detach(moar_token)
88158
detach(honey_token)
89159
self.assertEqual(get_all_baggage(), {})
160+
161+
@staticmethod
162+
def has_prefix(baggage_key: str) -> bool:
163+
return baggage_key.startswith("que")
164+
165+
@staticmethod
166+
def matches_regex(baggage_key: str) -> bool:
167+
return re.match(r"que.*", baggage_key) is not None

0 commit comments

Comments
 (0)