Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DB drivers: db.statement inclusion of sqlcomment as opt-in #3121

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3105](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3105))


### Breaking changes

- `opentelemetry-instrumentation-dbapi` including sqlcomment in `db.statement` span attribute value is now opt-in
([#3115](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3115))
- `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-psycopg`, `opentelemetry-instrumentation-mysqlclient`, `opentelemetry-instrumentation-pymysql`: including sqlcomment in `db.statement` span attribute value is now opt-in
([#3121](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3121))


## Version 1.29.0/0.50b0 (2024-12-11)

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def trace_integration(
capture_parameters: bool = False,
enable_commenter: bool = False,
db_api_integration_factory=None,
enable_attribute_commenter: bool = False,
):
"""Integrate with DB API library.
https://www.python.org/dev/peps/pep-0249/
Expand All @@ -88,6 +89,7 @@ def trace_integration(
enable_commenter: Flag to enable/disable sqlcommenter.
db_api_integration_factory: The `DatabaseApiIntegration` to use. If none is passed the
default one is used.
enable_attribute_commenter: Flag to enable/disable sqlcomment inclusion in `db.statement` span attribute. Only available if enable_commenter=True.
"""
wrap_connect(
__name__,
Expand All @@ -100,6 +102,7 @@ def trace_integration(
capture_parameters=capture_parameters,
enable_commenter=enable_commenter,
db_api_integration_factory=db_api_integration_factory,
enable_attribute_commenter=enable_attribute_commenter,
)


Expand All @@ -115,6 +118,7 @@ def wrap_connect(
enable_commenter: bool = False,
db_api_integration_factory=None,
commenter_options: dict = None,
enable_attribute_commenter: bool = False,
):
"""Integrate with DB API library.
https://www.python.org/dev/peps/pep-0249/
Expand All @@ -133,6 +137,7 @@ def wrap_connect(
db_api_integration_factory: The `DatabaseApiIntegration` to use. If none is passed the
default one is used.
commenter_options: Configurations for tags to be appended at the sql query.
enable_attribute_commenter: Flag to enable/disable sqlcomment inclusion in `db.statement` span attribute. Only available if enable_commenter=True.

"""
db_api_integration_factory = (
Expand All @@ -156,6 +161,7 @@ def wrap_connect_(
enable_commenter=enable_commenter,
commenter_options=commenter_options,
connect_module=connect_module,
enable_attribute_commenter=enable_attribute_commenter,
)
return db_integration.wrapped_connection(wrapped, args, kwargs)

Expand Down Expand Up @@ -191,6 +197,7 @@ def instrument_connection(
enable_commenter: bool = False,
commenter_options: dict = None,
connect_module: typing.Callable[..., typing.Any] = None,
enable_attribute_commenter: bool = False,
):
"""Enable instrumentation in a database connection.

Expand All @@ -206,6 +213,7 @@ def instrument_connection(
enable_commenter: Flag to enable/disable sqlcommenter.
commenter_options: Configurations for tags to be appended at the sql query.
connect_module: Module name where connect method is available.
enable_attribute_commenter: Flag to enable/disable sqlcomment inclusion in `db.statement` span attribute. Only available if enable_commenter=True.

Returns:
An instrumented connection.
Expand All @@ -224,6 +232,7 @@ def instrument_connection(
enable_commenter=enable_commenter,
commenter_options=commenter_options,
connect_module=connect_module,
enable_attribute_commenter=enable_attribute_commenter,
)
db_integration.get_connection_attributes(connection)
return get_traced_connection_proxy(connection, db_integration)
Expand Down Expand Up @@ -257,6 +266,7 @@ def __init__(
enable_commenter: bool = False,
commenter_options: dict = None,
connect_module: typing.Callable[..., typing.Any] = None,
enable_attribute_commenter: bool = False,
):
self.connection_attributes = connection_attributes
if self.connection_attributes is None:
Expand All @@ -277,6 +287,7 @@ def __init__(
self.capture_parameters = capture_parameters
self.enable_commenter = enable_commenter
self.commenter_options = commenter_options
self.enable_attribute_commenter = enable_attribute_commenter
self.database_system = database_system
self.connection_props = {}
self.span_attributes = {}
Expand Down Expand Up @@ -434,9 +445,52 @@ def __init__(self, db_api_integration: DatabaseApiIntegration) -> None:
if self._db_api_integration.commenter_options
else {}
)
self._enable_attribute_commenter = (
self._db_api_integration.enable_attribute_commenter
)
self._connect_module = self._db_api_integration.connect_module
self._leading_comment_remover = re.compile(r"^/\*.*?\*/")

def _capture_mysql_version(self, cursor) -> None:
"""Lazy capture of mysql-connector client version using cursor, if applicable"""
if (
self._db_api_integration.database_system == "mysql"
and self._db_api_integration.connect_module.__name__
== "mysql.connector"
and not self._db_api_integration.commenter_data[
"mysql_client_version"
]
):
self._db_api_integration.commenter_data["mysql_client_version"] = (
cursor._cnx._cmysql.get_client_info()
)

def _get_commenter_data(self) -> dict:
"""Uses DB-API integration to return commenter data for sqlcomment"""
commenter_data = dict(self._db_api_integration.commenter_data)
if self._commenter_options.get("opentelemetry_values", True):
commenter_data.update(**_get_opentelemetry_values())
return {
k: v
for k, v in commenter_data.items()
if self._commenter_options.get(k, True)
}

def _update_args_with_added_sql_comment(self, args, cursor) -> tuple:
"""Updates args with cursor info and adds sqlcomment to query statement"""
try:
args_list = list(args)
self._capture_mysql_version(cursor)
commenter_data = self._get_commenter_data()
statement = _add_sql_comment(args_list[0], **commenter_data)
args_list[0] = statement
args = tuple(args_list)
except Exception as exc: # pylint: disable=broad-except
_logger.exception(
"Exception while generating sql comment: %s", exc
)
return args

def _populate_span(
self,
span: trace_api.Span,
Expand Down Expand Up @@ -497,52 +551,22 @@ def traced_execution(
) as span:
if span.is_recording():
if args and self._commenter_enabled:
try:
args_list = list(args)

# lazy capture of mysql-connector client version using cursor
if (
self._db_api_integration.database_system == "mysql"
and self._db_api_integration.connect_module.__name__
== "mysql.connector"
and not self._db_api_integration.commenter_data[
"mysql_client_version"
]
):
self._db_api_integration.commenter_data[
"mysql_client_version"
] = cursor._cnx._cmysql.get_client_info()

commenter_data = dict(
self._db_api_integration.commenter_data
)
if self._commenter_options.get(
"opentelemetry_values", True
):
commenter_data.update(
**_get_opentelemetry_values()
)

# Filter down to just the requested attributes.
commenter_data = {
k: v
for k, v in commenter_data.items()
if self._commenter_options.get(k, True)
}
statement = _add_sql_comment(
args_list[0], **commenter_data
if self._enable_attribute_commenter:
# sqlcomment is added to executed query and db.statement span attribute
args = self._update_args_with_added_sql_comment(
args, cursor
)

args_list[0] = statement
args = tuple(args_list)

except Exception as exc: # pylint: disable=broad-except
_logger.exception(
"Exception while generating sql comment: %s", exc
self._populate_span(span, cursor, *args)
else:
# sqlcomment is only added to executed query
# so db.statement is set before add_sql_comment
self._populate_span(span, cursor, *args)
args = self._update_args_with_added_sql_comment(
args, cursor
)

self._populate_span(span, cursor, *args)

else:
# no sqlcomment anywhere
self._populate_span(span, cursor, *args)
return query_method(*args, **kwargs)


Expand Down
Loading
Loading