Skip to content

Commit 48d5182

Browse files
authored
Set OTEL status description when logging exceptions (#348)
1 parent 3fbea2f commit 48d5182

File tree

2 files changed

+25
-7
lines changed

2 files changed

+25
-7
lines changed

logfire/_internal/main.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from opentelemetry.metrics import CallbackT, Counter, Histogram, UpDownCounter
1717
from opentelemetry.sdk.trace import ReadableSpan, Span
1818
from opentelemetry.semconv.trace import SpanAttributes
19-
from opentelemetry.trace import Tracer
19+
from opentelemetry.trace import StatusCode, Tracer
2020
from opentelemetry.util import types as otel_types
2121
from typing_extensions import LiteralString, ParamSpec
2222

@@ -26,13 +26,15 @@
2626
from .config import GLOBAL_CONFIG, LogfireConfig
2727
from .constants import (
2828
ATTRIBUTES_JSON_SCHEMA_KEY,
29+
ATTRIBUTES_LOG_LEVEL_NUM_KEY,
2930
ATTRIBUTES_MESSAGE_KEY,
3031
ATTRIBUTES_MESSAGE_TEMPLATE_KEY,
3132
ATTRIBUTES_SAMPLE_RATE_KEY,
3233
ATTRIBUTES_SPAN_TYPE_KEY,
3334
ATTRIBUTES_TAGS_KEY,
3435
ATTRIBUTES_VALIDATION_ERROR_KEY,
3536
DISABLE_CONSOLE_KEY,
37+
LEVEL_NUMBERS,
3638
NULL_ARGS_KEY,
3739
OTLP_MAX_INT_SIZE,
3840
LevelName,
@@ -663,6 +665,11 @@ def log(
663665
exc_info = exc_info[1]
664666
if isinstance(exc_info, BaseException):
665667
_record_exception(span, exc_info)
668+
if otlp_attributes[ATTRIBUTES_LOG_LEVEL_NUM_KEY] >= LEVEL_NUMBERS['error']: # type: ignore
669+
# Set the status description to the exception message.
670+
# OTEL only lets us set the description when the status code is ERROR,
671+
# which we only want to do when the log level is error.
672+
_set_exception_status(span, exc_info)
666673
elif exc_info is not None: # pragma: no cover
667674
raise TypeError(f'Invalid type for exc_info: {exc_info.__class__.__name__}')
668675

@@ -1773,6 +1780,15 @@ def _exit_span(span: trace_api.Span, exception: BaseException | None) -> None:
17731780
_record_exception(span, exception, escaped=True)
17741781

17751782

1783+
def _set_exception_status(span: trace_api.Span, exception: BaseException):
1784+
span.set_status(
1785+
trace_api.Status(
1786+
status_code=StatusCode.ERROR,
1787+
description=f'{exception.__class__.__name__}: {exception}',
1788+
)
1789+
)
1790+
1791+
17761792
@handle_internal_errors()
17771793
def _record_exception(
17781794
span: trace_api.Span,
@@ -1788,12 +1804,7 @@ def _record_exception(
17881804
# This means we know that the exception hasn't been handled,
17891805
# so we can set the OTEL status and the log level to error.
17901806
if escaped:
1791-
span.set_status(
1792-
trace_api.Status(
1793-
status_code=trace_api.StatusCode.ERROR,
1794-
description=f'{exception.__class__.__name__}: {exception}',
1795-
)
1796-
)
1807+
_set_exception_status(span, exception)
17971808
span.set_attributes(log_level_attributes('error'))
17981809

17991810
attributes = {**(attributes or {})}

tests/test_logfire.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,13 @@ def test_exc_info(exporter: TestExporter):
16471647
'exception.escaped': 'False',
16481648
}
16491649

1650+
for span in exporter.exported_spans[:-3]:
1651+
assert span.status.description is None
1652+
1653+
for span in exporter.exported_spans[-3:]:
1654+
assert span.status.status_code == StatusCode.ERROR
1655+
assert span.status.description == 'ValueError: an error'
1656+
16501657

16511658
def test_span_level(exporter: TestExporter):
16521659
with logfire.span('foo', _level='debug') as span:

0 commit comments

Comments
 (0)