diff --git a/CHANGELOG.md b/CHANGELOG.md index b0a167eb85..290e67bf5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3904](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3904)) - build: bump ruff to 0.14.1 ([#3842](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3842)) +- `opentelemetry-instrumentation-pymongo`: Fix invalid mongodb collection attribute type + ([#3942](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3942)) ## 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 af2a3f7f02..2a9eeed281 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py @@ -138,7 +138,7 @@ def started(self, event: monitoring.CommandStartedEvent): command_name = event.command_name span_name = f"{event.database_name}.{command_name}" statement = self._get_statement_by_command_name(command_name, event) - collection = event.command.get(event.command_name) + collection = _get_command_collection_name(event) try: span = self._tracer.start_span(span_name, kind=SpanKind.CLIENT) @@ -226,6 +226,13 @@ def _get_statement_by_command_name( return statement +def _get_command_collection_name(event: CommandEvent) -> str | None: + collection_name = event.command.get(event.command_name) + if not collection_name or not isinstance(collection_name, str): + return None + return collection_name + + def _get_span_dict_key( event: CommandEvent, ) -> int | tuple[int, tuple[str, int | None]]: diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py index 9d9f817466..06c5d5bb8f 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py @@ -278,6 +278,43 @@ def test_capture_statement_disabled_aggregate(self): span.attributes[SpanAttributes.DB_STATEMENT], "aggregate" ) + def test_collection_name_attribute(self): + scenarios = [ + ( + { + "command_name": "find", + "find": "test_collection", + }, + "test_collection", + ), + ({"command_name": "find"}, None), + ({"command_name": "find", "find": b"invalid"}, None), + ] + for command_attrs, expected in scenarios: + with self.subTest(command_attrs=command_attrs, expected=expected): + mock_event = MockEvent(command_attrs) + + command_tracer = CommandTracer( + self.tracer, capture_statement=True + ) + command_tracer.started(event=mock_event) + command_tracer.succeeded(event=mock_event) + + spans_list = self.memory_exporter.get_finished_spans() + + self.assertEqual(len(spans_list), 1) + span = spans_list[0] + + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], "find" + ) + + self.assertEqual( + span.attributes.get(SpanAttributes.DB_MONGODB_COLLECTION), + expected, + ) + self.memory_exporter.clear() + class MockCommand: def __init__(self, command_attrs):