Skip to content

Commit cf6d45e

Browse files
DB-API: db.statement inclusion of sqlcomment as opt-in (#3115)
1 parent 26bcc93 commit cf6d45e

File tree

3 files changed

+479
-45
lines changed

3 files changed

+479
-45
lines changed

Diff for: CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4242
- `opentelemetry-instrumentation-sqlalchemy` including sqlcomment in `db.statement` span attribute value is now opt-in
4343
([#3112](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3112))
4444

45+
### Breaking changes
46+
47+
- `opentelemetry-instrumentation-dbapi` including sqlcomment in `db.statement` span attribute value is now opt-in
48+
([#3115](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3115))
49+
50+
4551
## Version 1.29.0/0.50b0 (2024-12-11)
4652

4753
### Added

Diff for: instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py

+68-44
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def trace_integration(
7171
capture_parameters: bool = False,
7272
enable_commenter: bool = False,
7373
db_api_integration_factory=None,
74+
enable_attribute_commenter: bool = False,
7475
):
7576
"""Integrate with DB API library.
7677
https://www.python.org/dev/peps/pep-0249/
@@ -88,6 +89,7 @@ def trace_integration(
8889
enable_commenter: Flag to enable/disable sqlcommenter.
8990
db_api_integration_factory: The `DatabaseApiIntegration` to use. If none is passed the
9091
default one is used.
92+
enable_attribute_commenter: Flag to enable/disable sqlcomment inclusion in `db.statement` span attribute. Only available if enable_commenter=True.
9193
"""
9294
wrap_connect(
9395
__name__,
@@ -100,6 +102,7 @@ def trace_integration(
100102
capture_parameters=capture_parameters,
101103
enable_commenter=enable_commenter,
102104
db_api_integration_factory=db_api_integration_factory,
105+
enable_attribute_commenter=enable_attribute_commenter,
103106
)
104107

105108

@@ -115,6 +118,7 @@ def wrap_connect(
115118
enable_commenter: bool = False,
116119
db_api_integration_factory=None,
117120
commenter_options: dict = None,
121+
enable_attribute_commenter: bool = False,
118122
):
119123
"""Integrate with DB API library.
120124
https://www.python.org/dev/peps/pep-0249/
@@ -133,6 +137,7 @@ def wrap_connect(
133137
db_api_integration_factory: The `DatabaseApiIntegration` to use. If none is passed the
134138
default one is used.
135139
commenter_options: Configurations for tags to be appended at the sql query.
140+
enable_attribute_commenter: Flag to enable/disable sqlcomment inclusion in `db.statement` span attribute. Only available if enable_commenter=True.
136141
137142
"""
138143
db_api_integration_factory = (
@@ -156,6 +161,7 @@ def wrap_connect_(
156161
enable_commenter=enable_commenter,
157162
commenter_options=commenter_options,
158163
connect_module=connect_module,
164+
enable_attribute_commenter=enable_attribute_commenter,
159165
)
160166
return db_integration.wrapped_connection(wrapped, args, kwargs)
161167

@@ -191,6 +197,7 @@ def instrument_connection(
191197
enable_commenter: bool = False,
192198
commenter_options: dict = None,
193199
connect_module: typing.Callable[..., typing.Any] = None,
200+
enable_attribute_commenter: bool = False,
194201
):
195202
"""Enable instrumentation in a database connection.
196203
@@ -206,6 +213,7 @@ def instrument_connection(
206213
enable_commenter: Flag to enable/disable sqlcommenter.
207214
commenter_options: Configurations for tags to be appended at the sql query.
208215
connect_module: Module name where connect method is available.
216+
enable_attribute_commenter: Flag to enable/disable sqlcomment inclusion in `db.statement` span attribute. Only available if enable_commenter=True.
209217
210218
Returns:
211219
An instrumented connection.
@@ -224,6 +232,7 @@ def instrument_connection(
224232
enable_commenter=enable_commenter,
225233
commenter_options=commenter_options,
226234
connect_module=connect_module,
235+
enable_attribute_commenter=enable_attribute_commenter,
227236
)
228237
db_integration.get_connection_attributes(connection)
229238
return get_traced_connection_proxy(connection, db_integration)
@@ -257,6 +266,7 @@ def __init__(
257266
enable_commenter: bool = False,
258267
commenter_options: dict = None,
259268
connect_module: typing.Callable[..., typing.Any] = None,
269+
enable_attribute_commenter: bool = False,
260270
):
261271
self.connection_attributes = connection_attributes
262272
if self.connection_attributes is None:
@@ -277,6 +287,7 @@ def __init__(
277287
self.capture_parameters = capture_parameters
278288
self.enable_commenter = enable_commenter
279289
self.commenter_options = commenter_options
290+
self.enable_attribute_commenter = enable_attribute_commenter
280291
self.database_system = database_system
281292
self.connection_props = {}
282293
self.span_attributes = {}
@@ -434,9 +445,52 @@ def __init__(self, db_api_integration: DatabaseApiIntegration) -> None:
434445
if self._db_api_integration.commenter_options
435446
else {}
436447
)
448+
self._enable_attribute_commenter = (
449+
self._db_api_integration.enable_attribute_commenter
450+
)
437451
self._connect_module = self._db_api_integration.connect_module
438452
self._leading_comment_remover = re.compile(r"^/\*.*?\*/")
439453

454+
def _capture_mysql_version(self, cursor) -> None:
455+
"""Lazy capture of mysql-connector client version using cursor, if applicable"""
456+
if (
457+
self._db_api_integration.database_system == "mysql"
458+
and self._db_api_integration.connect_module.__name__
459+
== "mysql.connector"
460+
and not self._db_api_integration.commenter_data[
461+
"mysql_client_version"
462+
]
463+
):
464+
self._db_api_integration.commenter_data["mysql_client_version"] = (
465+
cursor._cnx._cmysql.get_client_info()
466+
)
467+
468+
def _get_commenter_data(self) -> dict:
469+
"""Uses DB-API integration to return commenter data for sqlcomment"""
470+
commenter_data = dict(self._db_api_integration.commenter_data)
471+
if self._commenter_options.get("opentelemetry_values", True):
472+
commenter_data.update(**_get_opentelemetry_values())
473+
return {
474+
k: v
475+
for k, v in commenter_data.items()
476+
if self._commenter_options.get(k, True)
477+
}
478+
479+
def _update_args_with_added_sql_comment(self, args, cursor) -> tuple:
480+
"""Updates args with cursor info and adds sqlcomment to query statement"""
481+
try:
482+
args_list = list(args)
483+
self._capture_mysql_version(cursor)
484+
commenter_data = self._get_commenter_data()
485+
statement = _add_sql_comment(args_list[0], **commenter_data)
486+
args_list[0] = statement
487+
args = tuple(args_list)
488+
except Exception as exc: # pylint: disable=broad-except
489+
_logger.exception(
490+
"Exception while generating sql comment: %s", exc
491+
)
492+
return args
493+
440494
def _populate_span(
441495
self,
442496
span: trace_api.Span,
@@ -497,52 +551,22 @@ def traced_execution(
497551
) as span:
498552
if span.is_recording():
499553
if args and self._commenter_enabled:
500-
try:
501-
args_list = list(args)
502-
503-
# lazy capture of mysql-connector client version using cursor
504-
if (
505-
self._db_api_integration.database_system == "mysql"
506-
and self._db_api_integration.connect_module.__name__
507-
== "mysql.connector"
508-
and not self._db_api_integration.commenter_data[
509-
"mysql_client_version"
510-
]
511-
):
512-
self._db_api_integration.commenter_data[
513-
"mysql_client_version"
514-
] = cursor._cnx._cmysql.get_client_info()
515-
516-
commenter_data = dict(
517-
self._db_api_integration.commenter_data
518-
)
519-
if self._commenter_options.get(
520-
"opentelemetry_values", True
521-
):
522-
commenter_data.update(
523-
**_get_opentelemetry_values()
524-
)
525-
526-
# Filter down to just the requested attributes.
527-
commenter_data = {
528-
k: v
529-
for k, v in commenter_data.items()
530-
if self._commenter_options.get(k, True)
531-
}
532-
statement = _add_sql_comment(
533-
args_list[0], **commenter_data
554+
if self._enable_attribute_commenter:
555+
# sqlcomment is added to executed query and db.statement span attribute
556+
args = self._update_args_with_added_sql_comment(
557+
args, cursor
534558
)
535-
536-
args_list[0] = statement
537-
args = tuple(args_list)
538-
539-
except Exception as exc: # pylint: disable=broad-except
540-
_logger.exception(
541-
"Exception while generating sql comment: %s", exc
559+
self._populate_span(span, cursor, *args)
560+
else:
561+
# sqlcomment is only added to executed query
562+
# so db.statement is set before add_sql_comment
563+
self._populate_span(span, cursor, *args)
564+
args = self._update_args_with_added_sql_comment(
565+
args, cursor
542566
)
543-
544-
self._populate_span(span, cursor, *args)
545-
567+
else:
568+
# no sqlcomment anywhere
569+
self._populate_span(span, cursor, *args)
546570
return query_method(*args, **kwargs)
547571

548572

0 commit comments

Comments
 (0)