Skip to content

opentelemetry-sdk: use stable code attributes #4508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4494](https://github.com/open-telemetry/opentelemetry-python/pull/4494))
- Improve CI by cancelling stale runs and setting timeouts
([#4498](https://github.com/open-telemetry/opentelemetry-python/pull/4498))
- opentelemetry-sdk: use release candidate code attributes
([#4508](https://github.com/open-telemetry/opentelemetry-python/pull/4508))

## Version 1.31.0/0.52b0 (2025-03-12)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.util import ns_to_iso_str
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.semconv._incubating.attributes import code_attributes
from opentelemetry.semconv.attributes import exception_attributes
from opentelemetry.trace import (
format_span_id,
format_trace_id,
Expand Down Expand Up @@ -483,22 +484,24 @@ def _get_attributes(record: logging.LogRecord) -> Attributes:
}

# Add standard code attributes for logs.
attributes[SpanAttributes.CODE_FILEPATH] = record.pathname
attributes[SpanAttributes.CODE_FUNCTION] = record.funcName
attributes[SpanAttributes.CODE_LINENO] = record.lineno
attributes[code_attributes.CODE_FILE_PATH] = record.pathname
attributes[code_attributes.CODE_FUNCTION_NAME] = record.funcName
attributes[code_attributes.CODE_LINE_NUMBER] = record.lineno

if record.exc_info:
exctype, value, tb = record.exc_info
if exctype is not None:
attributes[SpanAttributes.EXCEPTION_TYPE] = exctype.__name__
attributes[exception_attributes.EXCEPTION_TYPE] = (
exctype.__name__
)
if value is not None and value.args:
attributes[SpanAttributes.EXCEPTION_MESSAGE] = str(
attributes[exception_attributes.EXCEPTION_MESSAGE] = str(
value.args[0]
)
if tb is not None:
# https://github.com/open-telemetry/opentelemetry-specification/blob/9fa7c656b26647b27e485a6af7e38dc716eba98a/specification/trace/semantic_conventions/exceptions.md#stacktrace-representation
attributes[SpanAttributes.EXCEPTION_STACKTRACE] = "".join(
traceback.format_exception(*record.exc_info)
attributes[exception_attributes.EXCEPTION_STACKTRACE] = (
"".join(traceback.format_exception(*record.exc_info))
)
return attributes

Expand Down
31 changes: 18 additions & 13 deletions opentelemetry-sdk/tests/logs/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
LoggingHandler,
LogRecordProcessor,
)
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.semconv._incubating.attributes import code_attributes
from opentelemetry.semconv.attributes import exception_attributes
from opentelemetry.trace import INVALID_SPAN_CONTEXT


Expand Down Expand Up @@ -127,17 +128,19 @@ def test_log_record_user_attributes(self):
self.assertEqual(len(log_record.attributes), 4)
self.assertEqual(log_record.attributes["http.status_code"], 200)
self.assertTrue(
log_record.attributes[SpanAttributes.CODE_FILEPATH].endswith(
log_record.attributes[code_attributes.CODE_FILE_PATH].endswith(
"test_handler.py"
)
)
self.assertEqual(
log_record.attributes[SpanAttributes.CODE_FUNCTION],
log_record.attributes[code_attributes.CODE_FUNCTION_NAME],
"test_log_record_user_attributes",
)
# The line of the log statement is not a constant (changing tests may change that),
# so only check that the attribute is present.
self.assertTrue(SpanAttributes.CODE_LINENO in log_record.attributes)
self.assertTrue(
code_attributes.CODE_LINE_NUMBER in log_record.attributes
)
self.assertTrue(isinstance(log_record.attributes, BoundedAttributes))

def test_log_record_exception(self):
Expand All @@ -155,15 +158,15 @@ def test_log_record_exception(self):
self.assertIsNotNone(log_record)
self.assertEqual(log_record.body, "Zero Division Error")
self.assertEqual(
log_record.attributes[SpanAttributes.EXCEPTION_TYPE],
log_record.attributes[exception_attributes.EXCEPTION_TYPE],
ZeroDivisionError.__name__,
)
self.assertEqual(
log_record.attributes[SpanAttributes.EXCEPTION_MESSAGE],
log_record.attributes[exception_attributes.EXCEPTION_MESSAGE],
"division by zero",
)
stack_trace = log_record.attributes[
SpanAttributes.EXCEPTION_STACKTRACE
exception_attributes.EXCEPTION_STACKTRACE
]
self.assertIsInstance(stack_trace, str)
self.assertTrue("Traceback" in stack_trace)
Expand All @@ -188,15 +191,15 @@ def test_log_record_recursive_exception(self):
self.assertIsNotNone(log_record)
self.assertEqual(log_record.body, "Zero Division Error")
self.assertEqual(
log_record.attributes[SpanAttributes.EXCEPTION_TYPE],
log_record.attributes[exception_attributes.EXCEPTION_TYPE],
ZeroDivisionError.__name__,
)
self.assertEqual(
log_record.attributes[SpanAttributes.EXCEPTION_MESSAGE],
log_record.attributes[exception_attributes.EXCEPTION_MESSAGE],
"division by zero",
)
stack_trace = log_record.attributes[
SpanAttributes.EXCEPTION_STACKTRACE
exception_attributes.EXCEPTION_STACKTRACE
]
self.assertIsInstance(stack_trace, str)
self.assertTrue("Traceback" in stack_trace)
Expand All @@ -218,12 +221,14 @@ def test_log_exc_info_false(self):

self.assertIsNotNone(log_record)
self.assertEqual(log_record.body, "Zero Division Error")
self.assertNotIn(SpanAttributes.EXCEPTION_TYPE, log_record.attributes)
self.assertNotIn(
SpanAttributes.EXCEPTION_MESSAGE, log_record.attributes
exception_attributes.EXCEPTION_TYPE, log_record.attributes
)
self.assertNotIn(
exception_attributes.EXCEPTION_MESSAGE, log_record.attributes
)
self.assertNotIn(
SpanAttributes.EXCEPTION_STACKTRACE, log_record.attributes
exception_attributes.EXCEPTION_STACKTRACE, log_record.attributes
)

def test_log_record_trace_correlation(self):
Expand Down