Skip to content

Commit ad15f7b

Browse files
authored
Logging instr hook (#1117)
1 parent 0bd8e4c commit ad15f7b

File tree

3 files changed

+43
-2
lines changed

3 files changed

+43
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- cleanup type hints for textmap `Getter` and `Setter` classes
1616
([1106](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1106))
1717

18+
1819
### Added
20+
- `opentelemetry-instrumentation-logging` add log hook support
21+
([#1117](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1117))
1922
- `opentelemetry-instrumentation-remoulade` Initial release
2023
([#1082](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1082))
21-
22-
### Added
2324
- Added `opentelemetry-instrumention-confluent-kafka`
2425
([#1111](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1111))
2526

instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class LoggingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring
5555
5656
{DEFAULT_LOGGING_FORMAT}
5757
58+
def log_hook(span: Span, record: LogRecord):
59+
if span and span.is_recording():
60+
record.custom_user_attribute_from_log_hook = "some-value"
61+
5862
Args:
5963
tracer_provider: Tracer provider instance that can be used to fetch a tracer.
6064
set_logging_format: When set to True, it calls logging.basicConfig() and sets a logging format.
@@ -66,11 +70,13 @@ class LoggingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring
6670
logging.WARN
6771
logging.ERROR
6872
logging.FATAL
73+
log_hook: execute custom logic when record is created
6974
7075
See `BaseInstrumentor`
7176
"""
7277

7378
_old_factory = None
79+
_log_hook = None
7480

7581
def instrumentation_dependencies(self) -> Collection[str]:
7682
return _instruments
@@ -80,6 +86,7 @@ def _instrument(self, **kwargs):
8086
provider = kwargs.get("tracer_provider", None) or get_tracer_provider()
8187
old_factory = logging.getLogRecordFactory()
8288
LoggingInstrumentor._old_factory = old_factory
89+
LoggingInstrumentor._log_hook = kwargs.get("log_hook", None)
8390

8491
service_name = None
8592

@@ -107,6 +114,14 @@ def record_factory(*args, **kwargs):
107114
if ctx != INVALID_SPAN_CONTEXT:
108115
record.otelSpanID = format(ctx.span_id, "016x")
109116
record.otelTraceID = format(ctx.trace_id, "032x")
117+
if callable(LoggingInstrumentor._log_hook):
118+
try:
119+
LoggingInstrumentor._log_hook( # pylint: disable=E1102
120+
span, record
121+
)
122+
except Exception: # pylint: disable=W0703
123+
pass
124+
110125
return record
111126

112127
logging.setLogRecordFactory(record_factory)

instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ def test_trace_context_injection(self):
6464
self.assertEqual(record.otelServiceName, "")
6565

6666

67+
def log_hook(span, record):
68+
record.custom_user_attribute_from_log_hook = "some-value"
69+
70+
6771
class TestLoggingInstrumentor(TestBase):
6872
@pytest.fixture(autouse=True)
6973
def inject_fixtures(self, caplog):
@@ -150,6 +154,27 @@ def test_custom_format_and_level_api(
150154
format="%(message)s span_id=%(otelSpanID)s", level=logging.WARNING
151155
)
152156

157+
def test_log_hook(self):
158+
LoggingInstrumentor().uninstrument()
159+
LoggingInstrumentor().instrument(
160+
set_logging_format=True,
161+
log_hook=log_hook,
162+
)
163+
with self.tracer.start_as_current_span("s1") as span:
164+
span_id = format(span.get_span_context().span_id, "016x")
165+
trace_id = format(span.get_span_context().trace_id, "032x")
166+
with self.caplog.at_level(level=logging.INFO):
167+
logger = logging.getLogger("test logger")
168+
logger.info("hello")
169+
self.assertEqual(len(self.caplog.records), 1)
170+
record = self.caplog.records[0]
171+
self.assertEqual(record.otelSpanID, span_id)
172+
self.assertEqual(record.otelTraceID, trace_id)
173+
self.assertEqual(record.otelServiceName, "unknown_service")
174+
self.assertEqual(
175+
record.custom_user_attribute_from_log_hook, "some-value"
176+
)
177+
153178
def test_uninstrumented(self):
154179
with self.tracer.start_as_current_span("s1") as span:
155180
span_id = format(span.get_span_context().span_id, "016x")

0 commit comments

Comments
 (0)