From 58da5f5645f09fdcfd80ebe331f11f2fcc74f1dc Mon Sep 17 00:00:00 2001 From: henry3260 Date: Thu, 16 Jul 2026 04:08:50 +0800 Subject: [PATCH] opentelemetry-instrumentation-aiopg: fix manual connection attributes --- .changelog/4821.fixed | 1 + .../aiopg/aiopg_integration.py | 4 +- .../instrumentation/aiopg/wrappers.py | 4 +- .../tests/test_aiopg_integration.py | 23 ++++++++++- .../tests/postgres/test_aiopg_functional.py | 40 +++++++++++++++++++ 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 .changelog/4821.fixed diff --git a/.changelog/4821.fixed b/.changelog/4821.fixed new file mode 100644 index 0000000000..4d46bd4308 --- /dev/null +++ b/.changelog/4821.fixed @@ -0,0 +1 @@ +`opentelemetry-instrumentation-aiopg`: fix connection metadata for manual instrumentation diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py index dc8a4708e3..de9a1254ad 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py @@ -107,9 +107,7 @@ async def traced_execution( *args: typing.Tuple[typing.Any, typing.Any], **kwargs: typing.Dict[typing.Any, typing.Any], ): - name = "" - if args: - name = self.get_operation_name(cursor, args) + name = self.get_operation_name(cursor, args) if not name: name = ( diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py index 76e5dffcff..0c3c48278e 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py @@ -153,7 +153,9 @@ def instrument_connection( version=version, tracer_provider=tracer_provider, ) - db_integration.get_connection_attributes(connection) + db_integration.get_connection_attributes( + getattr(connection, "_conn", connection) + ) return get_traced_connection_proxy(connection, db_integration) diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py index 9a2be09c5b..eee0636729 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 import asyncio import logging +from types import SimpleNamespace from unittest import mock from unittest.mock import MagicMock @@ -188,7 +189,14 @@ def test_custom_tracer_provider_create_pool(self): self.assertIs(span.resource, resource) def test_instrument_connection(self): - cnx = async_call(aiopg.connect(database="test")) + cnx = async_call( + aiopg.connect( + database="testdatabase", + server_host="testhost", + server_port=123, + user="testuser", + ) + ) query = "SELECT * FROM test" cursor = async_call(cnx.cursor()) async_call(cursor.execute(query)) @@ -202,6 +210,11 @@ def test_instrument_connection(self): spans_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans_list), 1) + span = spans_list[0] + self.assertEqual(span.attributes[DB_NAME], "testdatabase") + self.assertEqual(span.attributes[DB_USER], "testuser") + self.assertEqual(span.attributes[NET_PEER_NAME], "testhost") + self.assertEqual(span.attributes[NET_PEER_PORT], 123) def test_instrument_connection_after_instrument(self): cnx = async_call(aiopg.connect(database="test")) @@ -458,6 +471,7 @@ def test_instrument_connection(self): connection = mock.Mock() # Avoid get_attributes failing because can't concatenate mock connection.database = "-" + connection._conn = connection connection2 = wrappers.instrument_connection( self.tracer, connection, "-" ) @@ -468,6 +482,7 @@ def test_uninstrument_connection(self): # Set connection.database to avoid a failure because mock can't # be concatenated connection.database = "-" + connection._conn = connection connection2 = wrappers.instrument_connection( self.tracer, connection, "-" ) @@ -534,6 +549,12 @@ def __init__(self, database, server_port, server_host, user): self.server_port = server_port self.server_host = server_host self.user = user + self.info = SimpleNamespace( + dbname=database, + port=server_port, + host=server_host, + user=user, + ) class MockConnection: diff --git a/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py b/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py index 69b471993b..d13d3abfdf 100644 --- a/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py +++ b/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py @@ -103,6 +103,46 @@ def test_callproc(self): self.validate_spans("test") +class TestFunctionalAiopgInstrumentConnection(TestBase): + def setUp(self): + super().setUp() + + async def connect(): + connection = await aiopg.connect( + dbname=POSTGRES_DB_NAME, + user=POSTGRES_USER, + password=POSTGRES_PASSWORD, + host=POSTGRES_HOST, + port=POSTGRES_PORT, + ) + return AiopgInstrumentor().instrument_connection( + connection, tracer_provider=self.tracer_provider + ) + + self._connection = async_call(connect()) + self._cursor = async_call(self._connection.cursor()) + + def tearDown(self): + self._cursor.close() + self._connection.close() + super().tearDown() + + def test_execute(self): + """Should emit connection attributes for a manually instrumented connection.""" + async_call(self._cursor.execute("SELECT 1")) + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + span = spans[0] + self.assertEqual(span.name, "SELECT") + self.assertEqual(span.kind, trace_api.SpanKind.CLIENT) + self.assertEqual(span.attributes[DB_SYSTEM], "postgresql") + self.assertEqual(span.attributes[DB_NAME], POSTGRES_DB_NAME) + self.assertEqual(span.attributes[DB_USER], POSTGRES_USER) + self.assertEqual(span.attributes[NET_PEER_NAME], POSTGRES_HOST) + self.assertEqual(span.attributes[NET_PEER_PORT], POSTGRES_PORT) + + class TestFunctionalAiopgCreatePool(TestBase): def setUp(self): super().setUp()