Skip to content

Commit 38da1cb

Browse files
committed
opentelemetry-instrumentation-dbapi: fix libpq version detection
This fixes a crash in the detection of libpq version in the psycopg module when we wrap a module that is not the one containing the pq module that has the version.
1 parent 205fd59 commit 38da1cb

File tree

2 files changed

+57
-12
lines changed

2 files changed

+57
-12
lines changed

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,25 @@ def calculate_commenter_data(self) -> dict[str, Any]:
350350
}
351351

352352
if self.database_system == "postgresql":
353-
if hasattr(self.connect_module, "__libpq_version__"):
354-
libpq_version = self.connect_module.__libpq_version__
355-
else:
356-
libpq_version = self.connect_module.pq.__build_version__
357-
commenter_data.update({"libpq_version": libpq_version})
353+
libpq_version = None
354+
# psycopg
355+
if hasattr(self.connect_module, "pq"):
356+
try:
357+
libpq_version = self.connect_module.pq.version()
358+
except Exception:
359+
pass
360+
361+
# psycopg2
362+
if libpq_version is None:
363+
# this the libpq version the client has been built against
364+
libpq_version = getattr(
365+
self.connect_module, "__libpq_version__", None
366+
)
367+
368+
# psycopg also instrument modules that are not the root one, in that case you
369+
# won't get the libpq_version
370+
if libpq_version is not None:
371+
commenter_data.update({"libpq_version": libpq_version})
358372
elif self.database_system == "mysql":
359373
mysqlc_version = ""
360374
if db_driver == "MySQLdb":

instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -456,12 +456,11 @@ def test_executemany_comment_stmt_enabled_matches_db_statement_attribute(
456456
).group()
457457
self.assertEqual(cursor_span_id, db_statement_span_id)
458458

459-
def test_compatible_build_version_psycopg_psycopg2_libpq(self):
459+
def test_compatible_build_version_psycopg2_libpq(self):
460460
connect_module = mock.MagicMock()
461461
connect_module.__name__ = "test"
462462
connect_module.__version__ = mock.MagicMock()
463-
connect_module.pq = mock.MagicMock()
464-
connect_module.pq.__build_version__ = 123
463+
connect_module.__libpq_version__ = 123
465464
connect_module.apilevel = 123
466465
connect_module.threadsafety = 123
467466
connect_module.paramstyle = "test"
@@ -490,14 +489,46 @@ def test_compatible_build_version_psycopg_psycopg2_libpq(self):
490489
"Select 1;",
491490
)
492491

493-
def test_compatible_build_version_psycopg_psycopg2_libpq_stmt_enabled(
492+
def test_compatible_build_version_psycopg_libpq(self):
493+
connect_module = mock.MagicMock()
494+
connect_module.__name__ = "test"
495+
connect_module.__version__ = mock.MagicMock()
496+
connect_module.pq.version.return_value = 123
497+
connect_module.apilevel = 123
498+
connect_module.threadsafety = 123
499+
connect_module.paramstyle = "test"
500+
501+
db_integration = dbapi.DatabaseApiIntegration(
502+
"instrumenting_module_test_name",
503+
"postgresql",
504+
enable_commenter=True,
505+
commenter_options={"db_driver": False, "dbapi_level": False},
506+
connect_module=connect_module,
507+
)
508+
mock_connection = db_integration.wrapped_connection(
509+
mock_connect, {}, {}
510+
)
511+
cursor = mock_connection.cursor()
512+
cursor.executemany("Select 1;")
513+
self.assertRegex(
514+
cursor.query,
515+
r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
516+
)
517+
spans_list = self.memory_exporter.get_finished_spans()
518+
self.assertEqual(len(spans_list), 1)
519+
span = spans_list[0]
520+
self.assertEqual(
521+
span.attributes[SpanAttributes.DB_STATEMENT],
522+
"Select 1;",
523+
)
524+
525+
def test_no_build_version_where_we_dont_instrument_the_root_module(
494526
self,
495527
):
496528
connect_module = mock.MagicMock()
497529
connect_module.__name__ = "test"
498530
connect_module.__version__ = mock.MagicMock()
499531
connect_module.pq = mock.MagicMock()
500-
connect_module.pq.__build_version__ = 123
501532
connect_module.apilevel = 123
502533
connect_module.threadsafety = 123
503534
connect_module.paramstyle = "test"
@@ -517,14 +548,14 @@ def test_compatible_build_version_psycopg_psycopg2_libpq_stmt_enabled(
517548
cursor.executemany("Select 1;")
518549
self.assertRegex(
519550
cursor.query,
520-
r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
551+
r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
521552
)
522553
spans_list = self.memory_exporter.get_finished_spans()
523554
self.assertEqual(len(spans_list), 1)
524555
span = spans_list[0]
525556
self.assertRegex(
526557
span.attributes[SpanAttributes.DB_STATEMENT],
527-
r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
558+
r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
528559
)
529560

530561
def test_executemany_psycopg2_integration_comment(self):

0 commit comments

Comments
 (0)