Skip to content

Commit d35b4d6

Browse files
committed
Add BaggageLogProcessor to opentelemetry-processor-baggage
1 parent 195bcdb commit d35b4d6

5 files changed

Lines changed: 208 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
## Unreleased
1313

1414
### Added
15-
15+
- Add `BaggageLogProcessor` to `opentelemetry-processor-baggage`
16+
([#4062](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/4062))
1617
- `opentelemetry-instrumentation-confluent-kafka`: Loosen confluent-kafka upper bound to <3.0.0
1718
([#4289](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4289))
1819
- `opentelemetry-instrumentation`: Add support for wrapt 2.x

processor/opentelemetry-processor-baggage/README.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,42 @@ For example, to only copy baggage entries that match the regex `^key.+`:
6565
regex_predicate = lambda baggage_key: baggage_key.startswith("^key.+")
6666
tracer_provider.add_span_processor(BaggageSpanProcessor(regex_predicate))
6767

68+
BaggageLogProcessor
69+
-------------------
70+
71+
The BaggageLogProcessor reads entries stored in Baggage
72+
from the current context and adds the baggage entries' keys and
73+
values to the log record as attributes on emit.
74+
75+
Add this log processor to a logger provider.
76+
77+
To configure the log processor to copy all baggage entries:
78+
79+
::
80+
81+
from opentelemetry.processor.baggage import BaggageLogProcessor, ALLOW_ALL_BAGGAGE_KEYS
82+
83+
logger_provider = LoggerProvider()
84+
logger_provider.add_log_record_processor(BaggageLogProcessor(ALLOW_ALL_BAGGAGE_KEYS))
85+
86+
87+
Alternatively, you can provide a custom baggage key predicate to select which baggage keys you want to copy.
88+
89+
For example, to only copy baggage entries that start with `my-key`:
90+
91+
::
92+
93+
starts_with_predicate = lambda baggage_key: baggage_key.startswith("my-key")
94+
logger_provider.add_log_record_processor(BaggageLogProcessor(starts_with_predicate))
95+
96+
97+
For example, to only copy baggage entries that match the regex `^key.+`:
98+
99+
::
100+
101+
regex_predicate = lambda baggage_key: re.match(r"^key.+", baggage_key) is not None
102+
logger_provider.add_log_record_processor(BaggageLogProcessor(regex_predicate))
103+
68104

69105
References
70106
----------

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
# limitations under the License.
1414

1515
# pylint: disable=import-error
16-
1716
from .processor import ALLOW_ALL_BAGGAGE_KEYS, BaggageSpanProcessor
17+
from .log_processor import BaggageLogProcessor
1818
from .version import __version__
19-
20-
__all__ = ["ALLOW_ALL_BAGGAGE_KEYS", "BaggageSpanProcessor", "__version__"]
19+
__all__ = ["ALLOW_ALL_BAGGAGE_KEYS", "BaggageSpanProcessor", "BaggageLogProcessor", "__version__"]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from opentelemetry.baggage import get_all as get_all_baggage
16+
from opentelemetry.sdk._logs import LogRecordProcessor, ReadWriteLogRecord
17+
from opentelemetry.processor.baggage.processor import (
18+
ALLOW_ALL_BAGGAGE_KEYS,
19+
BaggageKeyPredicateT,
20+
)
21+
22+
23+
class BaggageLogProcessor(LogRecordProcessor):
24+
"""
25+
The BaggageLogProcessor reads entries stored in Baggage
26+
from the current context and adds the baggage entries' keys and
27+
values to the log record as attributes on emit.
28+
29+
Add this log processor to a logger provider.
30+
31+
⚠ Warning ⚠️
32+
33+
Do not put sensitive information in Baggage.
34+
35+
To repeat: a consequence of adding data to Baggage is that the keys and
36+
values will appear in all outgoing HTTP headers from the application.
37+
"""
38+
39+
def __init__(self, baggage_key_predicate: BaggageKeyPredicateT) -> None:
40+
self._baggage_key_predicate = baggage_key_predicate
41+
42+
def on_emit(self, log_record: ReadWriteLogRecord) -> None:
43+
baggage = get_all_baggage()
44+
for key, value in baggage.items():
45+
if self._baggage_key_predicate(key):
46+
log_record.log_record.attributes[key] = value
47+
48+
def shutdown(self) -> None:
49+
pass
50+
51+
def force_flush(self, timeout_millis: int = 30000) -> bool:
52+
return True
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import re
16+
import unittest
17+
18+
from opentelemetry.baggage import set_baggage
19+
from opentelemetry.context import attach, detach
20+
from opentelemetry.processor.baggage import (
21+
ALLOW_ALL_BAGGAGE_KEYS,
22+
BaggageLogProcessor,
23+
)
24+
from opentelemetry.sdk._logs import LoggerProvider, LogRecordProcessor
25+
from opentelemetry.sdk._logs.export import (
26+
InMemoryLogRecordExporter,
27+
BatchLogRecordProcessor,
28+
)
29+
30+
class BaggageLogProcessorTest(unittest.TestCase):
31+
def setUp(self):
32+
self.exporter = InMemoryLogRecordExporter()
33+
self.logger_provider = LoggerProvider()
34+
self.logger_provider.add_log_record_processor(
35+
BaggageLogProcessor(ALLOW_ALL_BAGGAGE_KEYS)
36+
)
37+
self.logger_provider.add_log_record_processor(
38+
BatchLogRecordProcessor(self.exporter)
39+
)
40+
self.logger = self.logger_provider.get_logger("test-logger")
41+
42+
def _get_attributes(self):
43+
self.logger_provider.force_flush()
44+
logs = self.exporter.get_finished_logs()
45+
self.assertTrue(len(logs) > 0)
46+
return logs[-1].log_record.attributes
47+
48+
def test_check_the_baggage(self):
49+
self.assertIsInstance(
50+
BaggageLogProcessor(ALLOW_ALL_BAGGAGE_KEYS), LogRecordProcessor
51+
)
52+
53+
def test_baggage_added_to_log_record(self):
54+
token = attach(set_baggage("queen", "bee"))
55+
self.logger.emit(None)
56+
attributes = self._get_attributes()
57+
self.assertEqual(attributes.get("queen"), "bee")
58+
detach(token)
59+
60+
def test_baggage_with_prefix(self):
61+
token = attach(set_baggage("queen", "bee"))
62+
logger_provider = LoggerProvider()
63+
logger_provider.add_log_record_processor(
64+
BaggageLogProcessor(lambda key: key.startswith("que"))
65+
)
66+
exporter = InMemoryLogRecordExporter()
67+
logger_provider.add_log_record_processor(
68+
BatchLogRecordProcessor(exporter)
69+
)
70+
logger = logger_provider.get_logger("test-logger")
71+
logger.emit(None)
72+
logger_provider.force_flush()
73+
logs = exporter.get_finished_logs()
74+
attributes = logs[-1].log_record.attributes
75+
self.assertEqual(attributes.get("queen"), "bee")
76+
detach(token)
77+
78+
def test_baggage_with_regex(self):
79+
token = attach(set_baggage("queen", "bee"))
80+
logger_provider = LoggerProvider()
81+
logger_provider.add_log_record_processor(
82+
BaggageLogProcessor(
83+
lambda key: re.match(r"que.*", key) is not None
84+
)
85+
)
86+
exporter = InMemoryLogRecordExporter()
87+
logger_provider.add_log_record_processor(
88+
BatchLogRecordProcessor(exporter)
89+
)
90+
logger = logger_provider.get_logger("test-logger")
91+
logger.emit(None)
92+
logger_provider.force_flush()
93+
logs = exporter.get_finished_logs()
94+
attributes = logs[-1].log_record.attributes
95+
self.assertEqual(attributes.get("queen"), "bee")
96+
detach(token)
97+
98+
def test_no_baggage_not_added(self):
99+
self.logger.emit(None)
100+
self.logger_provider.force_flush()
101+
logs = self.exporter.get_finished_logs()
102+
self.assertTrue(len(logs) > 0)
103+
attributes = logs[-1].log_record.attributes
104+
self.assertNotIn("queen", attributes)
105+
106+
@staticmethod
107+
def has_prefix(baggage_key: str) -> bool:
108+
return baggage_key.startswith("que")
109+
110+
@staticmethod
111+
def matches_regex(baggage_key: str) -> bool:
112+
return re.match(r"que.*", baggage_key) is not None
113+
114+
115+
if __name__ == "__main__":
116+
unittest.main()

0 commit comments

Comments
 (0)