Skip to content

Commit 2c75bc0

Browse files
Add _is_mysql_connector_cursor_prepared
1 parent d8b1ebf commit 2c75bc0

File tree

1 file changed

+85
-39
lines changed
  • instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi

1 file changed

+85
-39
lines changed

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

Lines changed: 85 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,34 @@ def _populate_span(
460460
if self._db_api_integration.capture_parameters and len(args) > 1:
461461
span.set_attribute("db.statement.parameters", str(args[1]))
462462

463+
def _is_mysql_connector_cursor_prepared(self, cursor): # pylint: disable=no-self-use
464+
try:
465+
from mysql.connector.cursor_cext import ( # pylint: disable=import-outside-toplevel
466+
CMySQLCursorPrepared,
467+
CMySQLCursorPreparedDict,
468+
CMySQLCursorPreparedNamedTuple,
469+
CMySQLCursorPreparedRaw,
470+
)
471+
472+
if type(cursor) in [
473+
CMySQLCursorPrepared,
474+
CMySQLCursorPreparedDict,
475+
CMySQLCursorPreparedNamedTuple,
476+
CMySQLCursorPreparedRaw,
477+
]:
478+
_logger.warning(
479+
"Adding sqlcomment to prepared MySQL statements is not supported. Please check OpenTelemetry configuration. Skipping."
480+
)
481+
return True
482+
483+
except ImportError as exc:
484+
_logger.warning(
485+
"Could not verify mysql.connector cursor, skipping sqlcomment: %s",
486+
exc,
487+
)
488+
489+
return False
490+
463491
def get_operation_name(self, cursor, args): # pylint: disable=no-self-use
464492
if args and isinstance(args[0], str):
465493
# Strip leading comments so we get the operation name.
@@ -494,49 +522,67 @@ def traced_execution(
494522
) as span:
495523
if span.is_recording():
496524
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
525+
# If a mysql-connector cursor was created with prepared=True
526+
# then MySQL statements will be prepared and executed natively.
527+
# 1:1 sqlcomment and span correlation in instrumentation will
528+
# break, so sqlcomment is not supported for this use case.
529+
# This is here because a client app can use multiple cursors.
530+
is_prepared = False
531+
if (
532+
self._db_api_integration.database_system == "mysql"
533+
and self._db_api_integration.connect_module.__name__
534+
== "mysql.connector"
535+
):
536+
is_prepared = self._is_mysql_connector_cursor_prepared(
537+
cursor
515538
)
516-
if self._commenter_options.get(
517-
"opentelemetry_values", True
518-
):
519-
commenter_data.update(
520-
**_get_opentelemetry_values()
521-
)
522539

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-
)
540+
if not is_prepared:
541+
try:
542+
args_list = list(args)
543+
544+
# lazy capture of mysql-connector client version using cursor
545+
if (
546+
self._db_api_integration.database_system
547+
== "mysql"
548+
and self._db_api_integration.connect_module.__name__
549+
== "mysql.connector"
550+
and not self._db_api_integration.commenter_data[
551+
"mysql_client_version"
552+
]
553+
):
554+
self._db_api_integration.commenter_data[
555+
"mysql_client_version"
556+
] = cursor._cnx._cmysql.get_client_info()
557+
558+
commenter_data = dict(
559+
self._db_api_integration.commenter_data
560+
)
561+
if self._commenter_options.get(
562+
"opentelemetry_values", True
563+
):
564+
commenter_data.update(
565+
**_get_opentelemetry_values()
566+
)
567+
568+
# Filter down to just the requested attributes.
569+
commenter_data = {
570+
k: v
571+
for k, v in commenter_data.items()
572+
if self._commenter_options.get(k, True)
573+
}
574+
statement = _add_sql_comment(
575+
args_list[0], **commenter_data
576+
)
532577

533-
args_list[0] = statement
534-
args = tuple(args_list)
578+
args_list[0] = statement
579+
args = tuple(args_list)
535580

536-
except Exception as exc: # pylint: disable=broad-except
537-
_logger.exception(
538-
"Exception while generating sql comment: %s", exc
539-
)
581+
except Exception as exc: # pylint: disable=broad-except
582+
_logger.exception(
583+
"Exception while generating sql comment: %s",
584+
exc,
585+
)
540586

541587
self._populate_span(span, cursor, *args)
542588

0 commit comments

Comments
 (0)