diff --git a/CHANGELOG.md b/CHANGELOG.md index 16ca9c2bbfe..3a15c93485a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3798](https://github.com/open-telemetry/opentelemetry-python/pull/3798)) - Fix otlp exporter to export log_record.observed_timestamp ([#3785](https://github.com/open-telemetry/opentelemetry-python/pull/3785)) +- Add capture the fully qualified type name for raised exceptions in spans + ([#3837](https://github.com/open-telemetry/opentelemetry-python/pull/3837)) ## Version 1.24.0/0.45b0 (2024-03-28) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 0110a5c0e05..c07f4f42c46 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -995,8 +995,15 @@ def record_exception( type(exception), value=exception, tb=exception.__traceback__ ) ) + module = type(exception).__module__ + qualname = type(exception).__qualname__ + exception_type = ( + f"{module}.{qualname}" + if module and module != "builtins" + else qualname + ) _attributes: MutableMapping[str, types.AttributeValue] = { - "exception.type": exception.__class__.__name__, + "exception.type": exception_type, "exception.message": str(exception), "exception.stacktrace": stacktrace, "exception.escaped": str(escaped), diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index 5441bed6dd7..72c7095da70 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -636,6 +636,10 @@ def test_events(self): self.assertEqual(span.events, tuple(events)) +class DummyError(Exception): + pass + + class TestSpan(unittest.TestCase): # pylint: disable=too-many-public-methods @@ -1144,6 +1148,25 @@ def error_status_test(context): .start_as_current_span("root") ) + def test_record_exception_fqn(self): + span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) + exception = DummyError("error") + exception_type = "tests.trace.test_trace.DummyError" + span.record_exception(exception) + exception_event = span.events[0] + self.assertEqual("exception", exception_event.name) + self.assertEqual( + "error", exception_event.attributes["exception.message"] + ) + self.assertEqual( + exception_type, + exception_event.attributes["exception.type"], + ) + self.assertIn( + "DummyError: error", + exception_event.attributes["exception.stacktrace"], + ) + def test_record_exception(self): span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) try: