Skip to content

Commit dbf3c4d

Browse files
tammy-baylis-swixrmx
authored andcommitted
DB-API instrumentor populates span after sqlcomment creation, not before (open-telemetry#2935)
1 parent 8e5a311 commit dbf3c4d

File tree

3 files changed

+89
-43
lines changed

3 files changed

+89
-43
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4444
([#2635](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2635))
4545
- `opentelemetry-instrumentation` Add support for string based dotted module paths in unwrap
4646
([#2919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2919))
47+
- `opentelemetry-instrumentation-dbapi` Add sqlcomment to `db.statement` attribute
48+
([#2935](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2935))
4749

4850
### Fixed
4951

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

+48-43
Original file line numberDiff line numberDiff line change
@@ -492,49 +492,54 @@ def traced_execution(
492492
with self._db_api_integration._tracer.start_as_current_span(
493493
name, kind=SpanKind.CLIENT
494494
) as span:
495-
self._populate_span(span, cursor, *args)
496-
if args and self._commenter_enabled:
497-
try:
498-
args_list = list(args)
499-
500-
# lazy capture of mysql-connector client version using cursor
501-
if (
502-
self._db_api_integration.database_system == "mysql"
503-
and self._db_api_integration.connect_module.__name__
504-
== "mysql.connector"
505-
and not self._db_api_integration.commenter_data[
506-
"mysql_client_version"
507-
]
508-
):
509-
self._db_api_integration.commenter_data[
510-
"mysql_client_version"
511-
] = cursor._cnx._cmysql.get_client_info()
512-
513-
commenter_data = dict(
514-
self._db_api_integration.commenter_data
515-
)
516-
if self._commenter_options.get(
517-
"opentelemetry_values", True
518-
):
519-
commenter_data.update(**_get_opentelemetry_values())
520-
521-
# Filter down to just the requested attributes.
522-
commenter_data = {
523-
k: v
524-
for k, v in commenter_data.items()
525-
if self._commenter_options.get(k, True)
526-
}
527-
statement = _add_sql_comment(
528-
args_list[0], **commenter_data
529-
)
530-
531-
args_list[0] = statement
532-
args = tuple(args_list)
533-
534-
except Exception as exc: # pylint: disable=broad-except
535-
_logger.exception(
536-
"Exception while generating sql comment: %s", exc
537-
)
495+
if span.is_recording():
496+
if args and self._commenter_enabled:
497+
try:
498+
args_list = list(args)
499+
500+
# lazy capture of mysql-connector client version using cursor
501+
if (
502+
self._db_api_integration.database_system == "mysql"
503+
and self._db_api_integration.connect_module.__name__
504+
== "mysql.connector"
505+
and not self._db_api_integration.commenter_data[
506+
"mysql_client_version"
507+
]
508+
):
509+
self._db_api_integration.commenter_data[
510+
"mysql_client_version"
511+
] = cursor._cnx._cmysql.get_client_info()
512+
513+
commenter_data = dict(
514+
self._db_api_integration.commenter_data
515+
)
516+
if self._commenter_options.get(
517+
"opentelemetry_values", True
518+
):
519+
commenter_data.update(
520+
**_get_opentelemetry_values()
521+
)
522+
523+
# Filter down to just the requested attributes.
524+
commenter_data = {
525+
k: v
526+
for k, v in commenter_data.items()
527+
if self._commenter_options.get(k, True)
528+
}
529+
statement = _add_sql_comment(
530+
args_list[0], **commenter_data
531+
)
532+
533+
args_list[0] = statement
534+
args = tuple(args_list)
535+
536+
except Exception as exc: # pylint: disable=broad-except
537+
_logger.exception(
538+
"Exception while generating sql comment: %s", exc
539+
)
540+
541+
self._populate_span(span, cursor, *args)
542+
538543
return query_method(*args, **kwargs)
539544

540545

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

+39
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515

1616
import logging
17+
import re
1718
from unittest import mock
1819

1920
from opentelemetry import context
@@ -306,6 +307,44 @@ def __getattr__(self, name):
306307
r"Select 1 /\*dbapi_level='1.0',dbapi_threadsafety='unknown',driver_paramstyle='unknown',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
307308
)
308309

310+
def test_executemany_comment_matches_db_statement_attribute(self):
311+
connect_module = mock.MagicMock()
312+
connect_module.__version__ = mock.MagicMock()
313+
connect_module.__libpq_version__ = 123
314+
connect_module.apilevel = 123
315+
connect_module.threadsafety = 123
316+
connect_module.paramstyle = "test"
317+
318+
db_integration = dbapi.DatabaseApiIntegration(
319+
"testname",
320+
"postgresql",
321+
enable_commenter=True,
322+
commenter_options={"db_driver": False, "dbapi_level": False},
323+
connect_module=connect_module,
324+
)
325+
mock_connection = db_integration.wrapped_connection(
326+
mock_connect, {}, {}
327+
)
328+
cursor = mock_connection.cursor()
329+
cursor.executemany("Select 1;")
330+
self.assertRegex(
331+
cursor.query,
332+
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}'\*/;",
333+
)
334+
spans_list = self.memory_exporter.get_finished_spans()
335+
self.assertEqual(len(spans_list), 1)
336+
span = spans_list[0]
337+
self.assertRegex(
338+
span.attributes[SpanAttributes.DB_STATEMENT],
339+
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}'\*/",
340+
)
341+
342+
cursor_span_id = re.search(r"[a-zA-Z0-9_]{16}", cursor.query).group()
343+
db_statement_span_id = re.search(
344+
r"[a-zA-Z0-9_]{16}", span.attributes[SpanAttributes.DB_STATEMENT]
345+
).group()
346+
self.assertEqual(cursor_span_id, db_statement_span_id)
347+
309348
def test_compatible_build_version_psycopg_psycopg2_libpq(self):
310349
connect_module = mock.MagicMock()
311350
connect_module.__name__ = "test"

0 commit comments

Comments
 (0)