Skip to content

Commit 9ca005d

Browse files
committed
Apply changes requested in code review
Signed-off-by: Israel Blancas <[email protected]>
1 parent bcafcc1 commit 9ca005d

File tree

4 files changed

+111
-24
lines changed

4 files changed

+111
-24
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ def __init__(
9595
severity_number: Optional[SeverityNumber] = None,
9696
body: AnyValue = None,
9797
attributes: Optional[_ExtendedAttributes] = None,
98-
exception: Optional[BaseException] = None,
9998
) -> None: ...
10099

101100
def __init__(
@@ -298,7 +297,7 @@ def emit(
298297
exception: BaseException | None = None,
299298
) -> None:
300299
if record:
301-
self._logger.emit(record)
300+
self._logger.emit(record, exception=exception)
302301
else:
303302
self._logger.emit(
304303
timestamp=timestamp,

opentelemetry-api/tests/logs/test_proxy.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# pylint: disable=W0212,W0222,W0221
1616
import typing
1717
import unittest
18+
from unittest.mock import Mock
1819

1920
import opentelemetry._logs._internal as _logs_internal
2021
from opentelemetry import _logs
@@ -75,3 +76,15 @@ def test_proxy_logger(self):
7576
# references to the old provider still work but return real logger now
7677
real_logger = provider.get_logger("proxy-test")
7778
self.assertIsInstance(real_logger, LoggerTest)
79+
80+
def test_proxy_logger_forwards_exception_with_record(self):
81+
logger = _logs_internal.ProxyLogger("proxy-test")
82+
logger._real_logger = Mock(spec=LoggerTest("proxy-test"))
83+
record = _logs.LogRecord()
84+
exception = ValueError("boom")
85+
86+
logger.emit(record, exception=exception)
87+
88+
logger._real_logger.emit.assert_called_once_with(
89+
record, exception=exception
90+
)

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

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -534,28 +534,50 @@ def _get_exception_attributes(
534534
}
535535

536536

537-
def _apply_exception_attributes(
538-
log_record: LogRecord,
537+
def _get_attributes_with_exception(
538+
attributes: _ExtendedAttributes | None,
539539
exception: BaseException | None,
540-
) -> None:
540+
) -> _ExtendedAttributes | None:
541541
if exception is None:
542-
return
542+
return attributes
543543

544544
exception_attributes_map = _get_exception_attributes(exception)
545-
attributes = log_record.attributes
546-
if attributes:
547-
if isinstance(attributes, BoundedAttributes):
548-
for key, value in exception_attributes_map.items():
549-
if key not in attributes:
550-
attributes[key] = value
551-
return
552-
merged = dict(attributes)
545+
attributes = attributes or {}
546+
if isinstance(attributes, BoundedAttributes):
547+
merged = BoundedAttributes(
548+
maxlen=attributes.maxlen,
549+
attributes=attributes,
550+
immutable=False,
551+
max_value_len=attributes.max_value_len,
552+
extended_attributes=attributes._extended_attributes, # pylint: disable=protected-access
553+
)
554+
merged.dropped = attributes.dropped
553555
for key, value in exception_attributes_map.items():
554-
merged.setdefault(key, value)
555-
log_record.attributes = merged
556-
return
557-
558-
log_record.attributes = exception_attributes_map
556+
if key not in merged:
557+
merged[key] = value
558+
return merged
559+
560+
return exception_attributes_map | dict(attributes)
561+
562+
563+
def _copy_log_record(
564+
record: LogRecord,
565+
attributes: _ExtendedAttributes | None,
566+
) -> LogRecord:
567+
return LogRecord(
568+
timestamp=record.timestamp,
569+
observed_timestamp=record.observed_timestamp,
570+
context=record.context,
571+
trace_id=record.trace_id,
572+
span_id=record.span_id,
573+
trace_flags=record.trace_flags,
574+
severity_text=record.severity_text,
575+
severity_number=record.severity_number,
576+
body=record.body,
577+
attributes=attributes,
578+
event_name=record.event_name,
579+
exception=getattr(record, "exception", None),
580+
)
559581

560582

561583
class LoggingHandler(logging.Handler):
@@ -725,32 +747,40 @@ def emit(
725747
record.log_record, "exception", None
726748
)
727749
if not isinstance(record, ReadWriteLogRecord):
728-
_apply_exception_attributes(record, record_exception)
750+
if record_exception is not None:
751+
record = _copy_log_record(
752+
record,
753+
_get_attributes_with_exception(
754+
record.attributes, record_exception
755+
),
756+
)
729757
# pylint:disable=protected-access
730758
writable_record = ReadWriteLogRecord._from_api_log_record(
731759
record=record,
732760
resource=self._resource,
733761
instrumentation_scope=self._instrumentation_scope,
734762
)
735763
else:
736-
_apply_exception_attributes(
737-
record.log_record, record_exception
764+
record.log_record.attributes = _get_attributes_with_exception(
765+
record.log_record.attributes, record_exception
738766
)
739767
writable_record = record
740768
else:
741769
# Create a record from individual parameters
770+
log_record_attributes = _get_attributes_with_exception(
771+
attributes, exception
772+
)
742773
log_record = LogRecord(
743774
timestamp=timestamp,
744775
observed_timestamp=observed_timestamp,
745776
context=context,
746777
severity_number=severity_number,
747778
severity_text=severity_text,
748779
body=body,
749-
attributes=attributes,
780+
attributes=log_record_attributes,
750781
event_name=event_name,
751782
exception=exception,
752783
)
753-
_apply_exception_attributes(log_record, exception)
754784
# pylint:disable=protected-access
755785
writable_record = ReadWriteLogRecord._from_api_log_record(
756786
record=log_record,

opentelemetry-sdk/tests/logs/test_logs.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from unittest.mock import Mock, patch
1919

2020
from opentelemetry._logs import LogRecord, SeverityNumber
21+
from opentelemetry.attributes import BoundedAttributes
2122
from opentelemetry.context import get_current
2223
from opentelemetry.sdk._logs import (
2324
Logger,
@@ -236,6 +237,22 @@ def test_emit_with_exception_adds_attributes(self):
236237
attributes[exception_attributes.EXCEPTION_STACKTRACE],
237238
)
238239

240+
def test_emit_with_raised_exception_has_stacktrace(self):
241+
logger, log_record_processor_mock = self._get_logger()
242+
243+
try:
244+
raise ValueError("boom")
245+
except ValueError as exc:
246+
logger.emit(body="error", exception=exc)
247+
248+
log_record_processor_mock.on_emit.assert_called_once()
249+
log_data = log_record_processor_mock.on_emit.call_args.args[0]
250+
stacktrace = dict(log_data.log_record.attributes)[
251+
exception_attributes.EXCEPTION_STACKTRACE
252+
]
253+
self.assertIn("Traceback (most recent call last)", stacktrace)
254+
self.assertIn("raise ValueError", stacktrace)
255+
239256
def test_emit_logrecord_exception_preserves_user_attributes(self):
240257
logger, log_record_processor_mock = self._get_logger()
241258
exc = ValueError("boom")
@@ -257,6 +274,34 @@ def test_emit_logrecord_exception_preserves_user_attributes(self):
257274
attributes[exception_attributes.EXCEPTION_MESSAGE], "boom"
258275
)
259276

277+
def test_emit_logrecord_exception_with_immutable_attributes(self):
278+
logger, log_record_processor_mock = self._get_logger()
279+
exc = ValueError("boom")
280+
original_attributes = BoundedAttributes(
281+
attributes={"custom": "value"},
282+
immutable=True,
283+
extended_attributes=True,
284+
)
285+
log_record = LogRecord(
286+
observed_timestamp=0,
287+
body="a log line",
288+
attributes=original_attributes,
289+
exception=exc,
290+
)
291+
292+
logger.emit(log_record)
293+
294+
self.assertNotIn(
295+
exception_attributes.EXCEPTION_TYPE, log_record.attributes
296+
)
297+
log_record_processor_mock.on_emit.assert_called_once()
298+
log_data = log_record_processor_mock.on_emit.call_args.args[0]
299+
attributes = dict(log_data.log_record.attributes)
300+
self.assertEqual(attributes["custom"], "value")
301+
self.assertEqual(
302+
attributes[exception_attributes.EXCEPTION_TYPE], "ValueError"
303+
)
304+
260305
def test_emit_readwrite_logrecord_uses_exception(self):
261306
logger, log_record_processor_mock = self._get_logger()
262307
exc = RuntimeError("kaput")

0 commit comments

Comments
 (0)