Skip to content

Commit b74633a

Browse files
authored
feat(pymongo): aggregate and getMore capture statements support (#3601)
* feat(pymongo): aggregate and getMore capture statements * chore: update changelog * fix: tests * fix: proper MockCommand and expectations * chore: ruff-format
1 parent b1c2c79 commit b74633a

File tree

3 files changed

+93
-6
lines changed

3 files changed

+93
-6
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222

2323
- `opentelemetry-util-http` Added support for redacting specific url query string values and url credentials in instrumentations
2424
([#3508](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3508))
25-
25+
- `opentelemetry-instrumentation-pymongo` `aggregate` and `getMore` capture statements support
26+
([#3601](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3601))
27+
2628
## Version 1.34.0/0.55b0 (2025-06-04)
2729

2830
### Fixed

instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@
1717
"delete": "deletes",
1818
"update": "updates",
1919
"find": "filter",
20+
"getMore": "collection",
21+
"aggregate": "pipeline",
2022
}

instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,12 @@ def test_started(self):
5757
# pylint: disable=protected-access
5858
span = command_tracer._pop_span(mock_event)
5959
self.assertIs(span.kind, trace_api.SpanKind.CLIENT)
60-
self.assertEqual(span.name, "database_name.command_name")
60+
self.assertEqual(span.name, "database_name.find")
6161
self.assertEqual(span.attributes[SpanAttributes.DB_SYSTEM], "mongodb")
6262
self.assertEqual(
6363
span.attributes[SpanAttributes.DB_NAME], "database_name"
6464
)
65-
self.assertEqual(
66-
span.attributes[SpanAttributes.DB_STATEMENT], "command_name"
67-
)
65+
self.assertEqual(span.attributes[SpanAttributes.DB_STATEMENT], "find")
6866
self.assertEqual(
6967
span.attributes[SpanAttributes.NET_PEER_NAME], "test.com"
7068
)
@@ -181,7 +179,7 @@ def test_int_command(self):
181179

182180
self.assertEqual(len(spans_list), 1)
183181
span = spans_list[0]
184-
self.assertEqual(span.name, "database_name.command_name")
182+
self.assertEqual(span.name, "database_name.123")
185183

186184
def test_no_op_tracer(self):
187185
mock_event = MockEvent({})
@@ -194,6 +192,90 @@ def test_no_op_tracer(self):
194192
spans_list = self.memory_exporter.get_finished_spans()
195193
self.assertEqual(len(spans_list), 0)
196194

195+
def test_capture_statement_getmore(self):
196+
command_attrs = {
197+
"command_name": "getMore",
198+
"collection": "test_collection",
199+
}
200+
mock_event = MockEvent(command_attrs)
201+
202+
command_tracer = CommandTracer(self.tracer, capture_statement=True)
203+
command_tracer.started(event=mock_event)
204+
command_tracer.succeeded(event=mock_event)
205+
206+
spans_list = self.memory_exporter.get_finished_spans()
207+
208+
self.assertEqual(len(spans_list), 1)
209+
span = spans_list[0]
210+
211+
self.assertEqual(
212+
span.attributes[SpanAttributes.DB_STATEMENT],
213+
"getMore test_collection",
214+
)
215+
216+
def test_capture_statement_aggregate(self):
217+
pipeline = [
218+
{"$match": {"status": "active"}},
219+
{"$group": {"_id": "$category", "count": {"$sum": 1}}},
220+
]
221+
command_attrs = {
222+
"command_name": "aggregate",
223+
"pipeline": pipeline,
224+
}
225+
command_tracer = CommandTracer(self.tracer, capture_statement=True)
226+
mock_event = MockEvent(command_attrs)
227+
command_tracer.started(event=mock_event)
228+
command_tracer.succeeded(event=mock_event)
229+
230+
spans_list = self.memory_exporter.get_finished_spans()
231+
232+
self.assertEqual(len(spans_list), 1)
233+
span = spans_list[0]
234+
235+
expected_statement = f"aggregate {pipeline}"
236+
self.assertEqual(
237+
span.attributes[SpanAttributes.DB_STATEMENT], expected_statement
238+
)
239+
240+
def test_capture_statement_disabled_getmore(self):
241+
command_attrs = {
242+
"command_name": "getMore",
243+
"collection": "test_collection",
244+
}
245+
command_tracer = CommandTracer(self.tracer, capture_statement=False)
246+
mock_event = MockEvent(command_attrs)
247+
command_tracer.started(event=mock_event)
248+
command_tracer.succeeded(event=mock_event)
249+
250+
spans_list = self.memory_exporter.get_finished_spans()
251+
252+
self.assertEqual(len(spans_list), 1)
253+
span = spans_list[0]
254+
255+
self.assertEqual(
256+
span.attributes[SpanAttributes.DB_STATEMENT], "getMore"
257+
)
258+
259+
def test_capture_statement_disabled_aggregate(self):
260+
pipeline = [{"$match": {"status": "active"}}]
261+
command_attrs = {
262+
"command_name": "aggregate",
263+
"pipeline": pipeline,
264+
}
265+
command_tracer = CommandTracer(self.tracer, capture_statement=False)
266+
mock_event = MockEvent(command_attrs)
267+
command_tracer.started(event=mock_event)
268+
command_tracer.succeeded(event=mock_event)
269+
270+
spans_list = self.memory_exporter.get_finished_spans()
271+
272+
self.assertEqual(len(spans_list), 1)
273+
span = spans_list[0]
274+
275+
self.assertEqual(
276+
span.attributes[SpanAttributes.DB_STATEMENT], "aggregate"
277+
)
278+
197279

198280
class MockCommand:
199281
def __init__(self, command_attrs):
@@ -206,6 +288,7 @@ def get(self, key, default=""):
206288
class MockEvent:
207289
def __init__(self, command_attrs, connection_id=None, request_id=""):
208290
self.command = MockCommand(command_attrs)
291+
self.command_name = self.command.get("command_name")
209292
self.connection_id = connection_id
210293
self.request_id = request_id
211294

0 commit comments

Comments
 (0)