diff --git a/CHANGELOG.md b/CHANGELOG.md index b36b689b60..0382235e90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3836](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3836)) - `opentelemetry-instrumentation-elasticsearch`: Enhance elasticsearch query body sanitization ([#3919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3919)) - +- `opentelemetry-instrumentation-pymongo`: Fix span error descriptions + ([#3904](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3904)) ## Version 1.38.0/0.59b0 (2025-10-16) diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py index 7ada8d789a..af2a3f7f02 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py @@ -198,7 +198,12 @@ def failed(self, event: monitoring.CommandFailedEvent): if span is None: return if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, event.failure)) + span.set_status( + Status( + StatusCode.ERROR, + event.failure.get("errmsg", "Unknown error"), + ) + ) try: self.failed_hook(span, event) except ( diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py index 8b082a4a14..9d9f817466 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py @@ -127,6 +127,7 @@ def test_failed(self): failed_hook=self.failed_callback, ) command_tracer.started(event=mock_event) + mock_event.mark_as_failed() command_tracer.failed(event=mock_event) spans_list = self.memory_exporter.get_finished_spans() @@ -137,7 +138,7 @@ def test_failed(self): span.status.status_code, trace_api.StatusCode.ERROR, ) - self.assertEqual(span.status.description, "failure") + self.assertEqual(span.status.description, "operation failed") self.assertIsNotNone(span.end_time) self.start_callback.assert_called_once() self.failed_callback.assert_called_once() @@ -149,6 +150,7 @@ def test_multiple_commands(self): command_tracer.started(event=first_mock_event) command_tracer.started(event=second_mock_event) command_tracer.succeeded(event=first_mock_event) + second_mock_event.mark_as_failed() command_tracer.failed(event=second_mock_event) spans_list = self.memory_exporter.get_finished_spans() @@ -291,6 +293,16 @@ def __init__(self, command_attrs, connection_id=None, request_id=""): self.command_name = self.command.get("command_name") self.connection_id = connection_id self.request_id = request_id + self.failure = None + + def mark_as_failed(self): + # CommandFailedEvent.failure is type _DocumentOut, which pymongo defines as: + # ``` + # _DocumentOut = Union[MutableMapping[str, Any], "RawBSONDocument"] + # ``` + # we go with the former, but both provide a `.get(key, default)` method. + # + self.failure = {"errmsg": "operation failed"} def __getattr__(self, item): return item