Skip to content

Commit 84f17b4

Browse files
authored
Merge branch 'main' into genai-utils-llminvocation-attributes
2 parents 51d8b42 + 1d97282 commit 84f17b4

File tree

5 files changed

+77
-2
lines changed

5 files changed

+77
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5050
([#3904](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3904))
5151
- build: bump ruff to 0.14.1
5252
([#3842](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3842))
53+
- `opentelemetry-instrumentation-redis`: Add default span name for pipeline operations
54+
([#3941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3941))
55+
- `opentelemetry-instrumentation-pymongo`: Fix invalid mongodb collection attribute type
56+
([#3942](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3942))
5357

5458
## Version 1.38.0/0.59b0 (2025-10-16)
5559

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def started(self, event: monitoring.CommandStartedEvent):
138138
command_name = event.command_name
139139
span_name = f"{event.database_name}.{command_name}"
140140
statement = self._get_statement_by_command_name(command_name, event)
141-
collection = event.command.get(event.command_name)
141+
collection = _get_command_collection_name(event)
142142

143143
try:
144144
span = self._tracer.start_span(span_name, kind=SpanKind.CLIENT)
@@ -226,6 +226,13 @@ def _get_statement_by_command_name(
226226
return statement
227227

228228

229+
def _get_command_collection_name(event: CommandEvent) -> str | None:
230+
collection_name = event.command.get(event.command_name)
231+
if not collection_name or not isinstance(collection_name, str):
232+
return None
233+
return collection_name
234+
235+
229236
def _get_span_dict_key(
230237
event: CommandEvent,
231238
) -> int | tuple[int, tuple[str, int | None]]:

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,43 @@ def test_capture_statement_disabled_aggregate(self):
278278
span.attributes[SpanAttributes.DB_STATEMENT], "aggregate"
279279
)
280280

281+
def test_collection_name_attribute(self):
282+
scenarios = [
283+
(
284+
{
285+
"command_name": "find",
286+
"find": "test_collection",
287+
},
288+
"test_collection",
289+
),
290+
({"command_name": "find"}, None),
291+
({"command_name": "find", "find": b"invalid"}, None),
292+
]
293+
for command_attrs, expected in scenarios:
294+
with self.subTest(command_attrs=command_attrs, expected=expected):
295+
mock_event = MockEvent(command_attrs)
296+
297+
command_tracer = CommandTracer(
298+
self.tracer, capture_statement=True
299+
)
300+
command_tracer.started(event=mock_event)
301+
command_tracer.succeeded(event=mock_event)
302+
303+
spans_list = self.memory_exporter.get_finished_spans()
304+
305+
self.assertEqual(len(spans_list), 1)
306+
span = spans_list[0]
307+
308+
self.assertEqual(
309+
span.attributes[SpanAttributes.DB_STATEMENT], "find"
310+
)
311+
312+
self.assertEqual(
313+
span.attributes.get(SpanAttributes.DB_MONGODB_COLLECTION),
314+
expected,
315+
)
316+
self.memory_exporter.clear()
317+
281318

282319
class MockCommand:
283320
def __init__(self, command_attrs):

instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,4 @@ def _build_span_meta_data_for_pipeline(
207207
resource = ""
208208
span_name = ""
209209

210-
return command_stack, resource, span_name
210+
return command_stack, resource, span_name or "redis"

instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,17 @@ def redis_operations():
390390
self.assertEqual(span.kind, SpanKind.CLIENT)
391391
self.assertEqual(span.status.status_code, trace.StatusCode.UNSET)
392392

393+
def test_span_name_empty_pipeline(self):
394+
redis_client = fakeredis.FakeStrictRedis()
395+
pipe = redis_client.pipeline()
396+
pipe.execute()
397+
398+
spans = self.memory_exporter.get_finished_spans()
399+
self.assertEqual(len(spans), 1)
400+
self.assertEqual(spans[0].name, "redis")
401+
self.assertEqual(spans[0].kind, SpanKind.CLIENT)
402+
self.assertEqual(spans[0].status.status_code, trace.StatusCode.UNSET)
403+
393404

394405
class TestRedisAsync(TestBase, IsolatedAsyncioTestCase):
395406
def assert_span_count(self, count: int):
@@ -543,6 +554,22 @@ def response_hook(span, conn, args):
543554
await self.client.set("key", "value")
544555
spans = self.assert_span_count(0)
545556

557+
@pytest.mark.asyncio
558+
async def test_span_name_empty_pipeline(self):
559+
redis_client = fakeredis.aioredis.FakeRedis()
560+
self.instrumentor.instrument_client(
561+
client=redis_client, tracer_provider=self.tracer_provider
562+
)
563+
async with redis_client.pipeline() as pipe:
564+
await pipe.execute()
565+
566+
spans = self.memory_exporter.get_finished_spans()
567+
self.assertEqual(len(spans), 1)
568+
self.assertEqual(spans[0].name, "redis")
569+
self.assertEqual(spans[0].kind, SpanKind.CLIENT)
570+
self.assertEqual(spans[0].status.status_code, trace.StatusCode.UNSET)
571+
self.instrumentor.uninstrument_client(client=redis_client)
572+
546573

547574
class TestRedisInstance(TestBase):
548575
def assert_span_count(self, count: int):

0 commit comments

Comments
 (0)