Skip to content

Commit ace9f7e

Browse files
committed
logs: remove context from LogRecord and pass to on_emit
- Remove context attribute from LogRecord to prevent memory inflation. - Update signature to accept context separately. - Update and SDK processors to align with the new signature. - Update tests to reflect the breaking changes. This aligns the Python SDK with the OpenTelemetry specification and resolves memory usage concerns in BatchLogRecordProcessor.
1 parent 7b38450 commit ace9f7e

8 files changed

Lines changed: 32 additions & 21 deletions

File tree

opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ def __init__(
118118
if observed_timestamp is None:
119119
observed_timestamp = time_ns()
120120
self.observed_timestamp = observed_timestamp
121-
self.context = context
122121
self.trace_id = trace_id or span_context.trace_id
123122
self.span_id = span_id or span_context.span_id
124123
self.trace_flags = trace_flags or span_context.trace_flags

opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def on_emit(self, log_record: ReadWriteLogRecord):
325325
"""
326326

327327
@abc.abstractmethod
328-
def on_emit(self, log_record: ReadWriteLogRecord):
328+
def on_emit(self, log_record: ReadWriteLogRecord, context: Context):
329329
"""Emits the ``ReadWriteLogRecord``.
330330
331331
Implementers should handle any exceptions raised during log processing
@@ -374,9 +374,9 @@ def add_log_record_processor(
374374
with self._lock:
375375
self._log_record_processors += (log_record_processor,)
376376

377-
def on_emit(self, log_record: ReadWriteLogRecord) -> None:
377+
def on_emit(self, log_record: ReadWriteLogRecord, context: Context) -> None:
378378
for lp in self._log_record_processors:
379-
lp.on_emit(log_record)
379+
lp.on_emit(log_record, context)
380380

381381
def shutdown(self) -> None:
382382
"""Shutdown the log processors one by one"""
@@ -448,8 +448,8 @@ def _submit_and_wait(
448448
for future in futures:
449449
future.result()
450450

451-
def on_emit(self, log_record: ReadWriteLogRecord):
452-
self._submit_and_wait(lambda lp: lp.on_emit, log_record)
451+
def on_emit(self, log_record: ReadWriteLogRecord, context: Context):
452+
self._submit_and_wait(lambda lp: lp.on_emit, log_record, context)
453453

454454
def shutdown(self):
455455
self._submit_and_wait(lambda lp: lp.shutdown)
@@ -675,6 +675,9 @@ def emit(
675675
"""Emits the :class:`ReadWriteLogRecord` by setting instrumentation scope
676676
and forwarding to the processor.
677677
"""
678+
if context is None:
679+
context = get_current()
680+
678681
# If a record is provided, use it directly
679682
if record is not None:
680683
if not isinstance(record, ReadWriteLogRecord):
@@ -706,7 +709,7 @@ def emit(
706709
)
707710

708711
self._logger_metrics.emit_log()
709-
self._multi_log_record_processor.on_emit(writable_record)
712+
self._multi_log_record_processor.on_emit(writable_record, context)
710713

711714

712715
class LoggerProvider(APILoggerProvider):

opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def __init__(self, exporter: LogRecordExporter):
174174
self._exporter = exporter
175175
self._shutdown = False
176176

177-
def on_emit(self, log_record: ReadWriteLogRecord):
177+
def on_emit(self, log_record: ReadWriteLogRecord, context: Context):
178178
# Prevent entering a recursive loop.
179179
cnt = get_value(_ON_EMIT_RECURSION_COUNT_KEY) or 0
180180
# Recursive depth of 3 is sort of arbitrary. It's possible that an Exporter.export call
@@ -278,7 +278,7 @@ def __init__(
278278
"Log",
279279
)
280280

281-
def on_emit(self, log_record: ReadWriteLogRecord) -> None:
281+
def on_emit(self, log_record: ReadWriteLogRecord, context: Context) -> None:
282282
# Convert ReadWriteLogRecord to ReadableLogRecord before passing to BatchProcessor
283283
# Note: resource should not be None at this point as it's set during Logger.emit()
284284
resource = (

opentelemetry-sdk/tests/logs/test_export.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from pytest import mark
2828

2929
from opentelemetry._logs import LogRecord, SeverityNumber
30+
from opentelemetry.context import get_current
3031
from opentelemetry.sdk import trace
3132
from opentelemetry.sdk._logs import (
3233
LoggerProvider,
@@ -433,7 +434,7 @@ def test_with_multiple_threads(self): # pylint: disable=no-self-use
433434

434435
def bulk_emit(num_emit):
435436
for _ in range(num_emit):
436-
batch_processor.on_emit(EMPTY_LOG)
437+
batch_processor.on_emit(EMPTY_LOG, get_current())
437438

438439
total_expected_logs = 0
439440
with ThreadPoolExecutor(max_workers=69) as executor:

opentelemetry-sdk/tests/logs/test_handler.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@
3232
from opentelemetry.semconv.attributes import exception_attributes
3333
from opentelemetry.trace import (
3434
INVALID_SPAN_CONTEXT,
35+
SpanContext,
36+
TraceFlags,
3537
set_span_in_context,
3638
)
39+
from opentelemetry.context.context import Context
3740

3841

3942
# pylint: disable=too-many-public-methods
@@ -336,6 +339,7 @@ def test_log_record_trace_correlation(self):
336339
logger.critical("Critical message within span")
337340

338341
record = processor.get_log_record(0)
342+
context = processor.contexts_emitted[0]
339343

340344
self.assertEqual(
341345
record.log_record.body,
@@ -346,7 +350,7 @@ def test_log_record_trace_correlation(self):
346350
record.log_record.severity_number,
347351
SeverityNumber.FATAL,
348352
)
349-
self.assertEqual(record.log_record.context, mock_context)
353+
self.assertEqual(context, mock_context)
350354
span_context = span.get_span_context()
351355
self.assertEqual(
352356
record.log_record.trace_id, span_context.trace_id
@@ -575,9 +579,11 @@ def set_up_test_logging(level, formatter=None, root_logger=False):
575579
class FakeProcessor(LogRecordProcessor):
576580
def __init__(self):
577581
self.log_data_emitted = []
582+
self.contexts_emitted = []
578583

579-
def on_emit(self, log_record: ReadableLogRecord):
584+
def on_emit(self, log_record: ReadableLogRecord, context: Context):
580585
self.log_data_emitted.append(log_record)
586+
self.contexts_emitted.append(context)
581587

582588
def shutdown(self):
583589
pass

opentelemetry-sdk/tests/logs/test_log_record.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ def test_log_record_from_api_log_record(self):
221221

222222
self.assertEqual(record.log_record.timestamp, 1)
223223
self.assertEqual(record.log_record.observed_timestamp, 2)
224-
self.assertEqual(record.log_record.context, get_current())
225224
# trace_id, span_id, and trace_flags come from the context's span
226225
self.assertEqual(record.log_record.trace_id, 0)
227226
self.assertEqual(record.log_record.span_id, 0)

opentelemetry-sdk/tests/logs/test_logs.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,10 @@ def test_can_emit_logrecord(self):
163163

164164
logger.emit(log_record)
165165
log_record_processor_mock.on_emit.assert_called_once()
166-
log_data = log_record_processor_mock.on_emit.call_args.args[0]
166+
log_data, context = log_record_processor_mock.on_emit.call_args.args
167167
self.assertTrue(isinstance(log_data.log_record, LogRecord))
168168
self.assertTrue(log_data.log_record is log_record)
169+
self.assertIsNotNone(context)
169170

170171
def test_can_emit_api_logrecord(self):
171172
logger, log_record_processor_mock = self._get_logger()
@@ -175,12 +176,12 @@ def test_can_emit_api_logrecord(self):
175176
)
176177
logger.emit(api_log_record)
177178
log_record_processor_mock.on_emit.assert_called_once()
178-
log_data = log_record_processor_mock.on_emit.call_args.args[0]
179+
log_data, context = log_record_processor_mock.on_emit.call_args.args
179180
log_record = log_data.log_record
180181
self.assertTrue(isinstance(log_record, LogRecord))
181182
self.assertEqual(log_record.timestamp, None)
182183
self.assertEqual(log_record.observed_timestamp, 0)
183-
self.assertIsNotNone(log_record.context)
184+
self.assertIsNotNone(context)
184185
self.assertEqual(log_record.severity_number, None)
185186
self.assertEqual(log_record.severity_text, None)
186187
self.assertEqual(log_record.body, "a log line")
@@ -203,12 +204,12 @@ def test_can_emit_with_keywords_arguments(self):
203204
)
204205
logger.emit(log_record)
205206
log_record_processor_mock.on_emit.assert_called_once()
206-
log_data = log_record_processor_mock.on_emit.call_args.args[0]
207+
log_data, context = log_record_processor_mock.on_emit.call_args.args
207208
result_log_record = log_data.log_record
208209
self.assertTrue(isinstance(result_log_record, LogRecord))
209210
self.assertEqual(result_log_record.timestamp, 100)
210211
self.assertEqual(result_log_record.observed_timestamp, 101)
211-
self.assertIsNotNone(result_log_record.context)
212+
self.assertIsNotNone(context)
212213
self.assertEqual(
213214
result_log_record.severity_number, SeverityNumber.WARN
214215
)

opentelemetry-sdk/tests/logs/test_multi_log_processor.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from unittest.mock import Mock
2323

2424
from opentelemetry._logs import LogRecord, SeverityNumber
25+
from opentelemetry.context import Context, get_current
2526
from opentelemetry.sdk._logs._internal import (
2627
ConcurrentMultiLogRecordProcessor,
2728
LoggerProvider,
@@ -38,7 +39,7 @@ def __init__(self, exporter, logs_list):
3839
self._log_list = logs_list
3940
self._closed = False
4041

41-
def on_emit(self, log_record: ReadWriteLogRecord):
42+
def on_emit(self, log_record: ReadWriteLogRecord, context: Context):
4243
if self._closed:
4344
return
4445
self._log_list.append(
@@ -123,9 +124,10 @@ def test_on_emit(self):
123124
for mock in mocks:
124125
multi_log_record_processor.add_log_record_processor(mock)
125126
record = self.make_record()
126-
multi_log_record_processor.on_emit(record)
127+
context = get_current()
128+
multi_log_record_processor.on_emit(record, context)
127129
for mock in mocks:
128-
mock.on_emit.assert_called_with(record)
130+
mock.on_emit.assert_called_with(record, context)
129131
multi_log_record_processor.shutdown()
130132

131133
def test_on_shutdown(self):

0 commit comments

Comments
 (0)