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

SQLAlchemy: db.statement inclusion of sqlcomment as opt-in #3112

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
48f238c
db.statement inclusion of sqlcomment as opt-in
tammy-baylis-swi Dec 16, 2024
fc8acd4
Changlog
tammy-baylis-swi Dec 16, 2024
89a31a2
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Dec 17, 2024
30d7b96
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Dec 17, 2024
a05acca
Add tests
tammy-baylis-swi Dec 17, 2024
d6b625e
Add tests
tammy-baylis-swi Dec 17, 2024
978cfa9
Fix test
tammy-baylis-swi Dec 17, 2024
63c5f12
Fix changelog
tammy-baylis-swi Dec 18, 2024
cc371a8
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
shalevr Dec 18, 2024
85c39a0
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Dec 18, 2024
888059f
Rearrange sqlcomment and span attr setup
tammy-baylis-swi Dec 19, 2024
7bcc404
More
tammy-baylis-swi Dec 19, 2024
5d21d6c
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Dec 19, 2024
3b97e64
Update doc with cardinality warning
tammy-baylis-swi Dec 20, 2024
8f45aaf
Update instrumentation/opentelemetry-instrumentation-sqlalchemy/src/o…
tammy-baylis-swi Dec 20, 2024
3f21e5e
Update instrumentation/opentelemetry-instrumentation-sqlalchemy/src/o…
tammy-baylis-swi Dec 20, 2024
2c8dbe3
Rm db.query.text from docs until semconv upgrade
tammy-baylis-swi Dec 20, 2024
520b5fe
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Dec 20, 2024
b6970b3
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Jan 2, 2025
0709012
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Jan 2, 2025
e552f6c
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Jan 2, 2025
748fb72
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Jan 7, 2025
091161b
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Jan 8, 2025
337aac5
Merge branch 'main' into sqlalchemy-db-statement-comment-opt-in
tammy-baylis-swi Jan 9, 2025
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `opentelemetry-instrumentation` Fix `get_dist_dependency_conflicts` if no distribution requires
([#3168](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3168))

### Breaking changes

- `opentelemetry-instrumentation-sqlalchemy` including sqlcomment in `db.statement` span attribute value is now opt-in
([#3112](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3112))

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,30 @@
::
Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/

SQLComment in span attribute
****************************
If sqlcommenter is enabled, you can further configure SQLAlchemy instrumentation to append sqlcomment to the `db.statement` span attribute for convenience of your platform.

.. code:: python

from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor

SQLAlchemyInstrumentor().instrument(
enable_commenter=True,
commenter_options={},
enable_attribute_commenter=True,
)


For example,
::

Invoking `engine.execute("select * from auth_users")` will lead to sql query "select * from auth_users" but when SQLCommenter and `attribute_commenter` is enabled
the query will get appended with some configurable tags like "select * from auth_users /*tag=value*/;" for both server query and `db.statement` span attribute.

Warning: capture of sqlcomment in ``db.statement`` may have high cardinality without platform normalization. See `Semantic Conventions for database spans <https://opentelemetry.io/docs/specs/semconv/database/database-spans/#generating-a-summary-of-the-query-text>`_ for more information.


Usage
-----
.. code:: python
Expand Down Expand Up @@ -138,6 +162,7 @@ def _instrument(self, **kwargs):
``meter_provider``: a MeterProvider, defaults to global
``enable_commenter``: bool to enable sqlcommenter, defaults to False
``commenter_options``: dict of sqlcommenter config, defaults to {}
``enable_attribute_commenter``: bool to enable sqlcomment addition to span attribute, defaults to False. Must also set `enable_commenter`.

Returns:
An instrumented engine if passed in as an argument or list of instrumented engines, None otherwise.
Expand Down Expand Up @@ -166,19 +191,30 @@ def _instrument(self, **kwargs):

enable_commenter = kwargs.get("enable_commenter", False)
commenter_options = kwargs.get("commenter_options", {})
enable_attribute_commenter = kwargs.get(
"enable_attribute_commenter", False
)

_w(
"sqlalchemy",
"create_engine",
_wrap_create_engine(
tracer, connections_usage, enable_commenter, commenter_options
tracer,
connections_usage,
enable_commenter,
commenter_options,
enable_attribute_commenter,
),
)
_w(
"sqlalchemy.engine",
"create_engine",
_wrap_create_engine(
tracer, connections_usage, enable_commenter, commenter_options
tracer,
connections_usage,
enable_commenter,
commenter_options,
enable_attribute_commenter,
),
)
# sqlalchemy.engine.create is not present in earlier versions of sqlalchemy (which we support)
Expand All @@ -191,6 +227,7 @@ def _instrument(self, **kwargs):
connections_usage,
enable_commenter,
commenter_options,
enable_attribute_commenter,
),
)
_w(
Expand All @@ -207,6 +244,7 @@ def _instrument(self, **kwargs):
connections_usage,
enable_commenter,
commenter_options,
enable_attribute_commenter,
),
)
if kwargs.get("engine") is not None:
Expand All @@ -216,6 +254,7 @@ def _instrument(self, **kwargs):
connections_usage,
kwargs.get("enable_commenter", False),
kwargs.get("commenter_options", {}),
kwargs.get("enable_attribute_commenter", False),
)
if kwargs.get("engines") is not None and isinstance(
kwargs.get("engines"), Sequence
Expand All @@ -227,6 +266,7 @@ def _instrument(self, **kwargs):
connections_usage,
kwargs.get("enable_commenter", False),
kwargs.get("commenter_options", {}),
kwargs.get("enable_attribute_commenter", False),
)
for engine in kwargs.get("engines")
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ def _normalize_vendor(vendor):


def _wrap_create_async_engine(
tracer, connections_usage, enable_commenter=False, commenter_options=None
tracer,
connections_usage,
enable_commenter=False,
commenter_options=None,
enable_attribute_commenter=False,
):
# pylint: disable=unused-argument
def _wrap_create_async_engine_internal(func, module, args, kwargs):
Expand All @@ -57,14 +61,19 @@ def _wrap_create_async_engine_internal(func, module, args, kwargs):
connections_usage,
enable_commenter,
commenter_options,
enable_attribute_commenter,
)
return engine

return _wrap_create_async_engine_internal


def _wrap_create_engine(
tracer, connections_usage, enable_commenter=False, commenter_options=None
tracer,
connections_usage,
enable_commenter=False,
commenter_options=None,
enable_attribute_commenter=False,
):
def _wrap_create_engine_internal(func, _module, args, kwargs):
"""Trace the SQLAlchemy engine, creating an `EngineTracer`
Expand All @@ -77,6 +86,7 @@ def _wrap_create_engine_internal(func, _module, args, kwargs):
connections_usage,
enable_commenter,
commenter_options,
enable_attribute_commenter,
)
return engine

Expand Down Expand Up @@ -110,12 +120,14 @@ def __init__(
connections_usage,
enable_commenter=False,
commenter_options=None,
enable_attribute_commenter=False,
):
self.tracer = tracer
self.connections_usage = connections_usage
self.vendor = _normalize_vendor(engine.name)
self.enable_commenter = enable_commenter
self.commenter_options = commenter_options if commenter_options else {}
self.enable_attribute_commenter = enable_attribute_commenter
self._engine_attrs = _get_attributes_from_engine(engine)
self._leading_comment_remover = re.compile(r"^/\*.*?\*/")

Expand Down Expand Up @@ -218,6 +230,32 @@ def _operation_name(self, db_name, statement):
return self.vendor
return " ".join(parts)

def _get_commenter_data(self, conn) -> dict:
"""Calculate sqlcomment contents from conn and configured options"""
commenter_data = {
"db_driver": conn.engine.driver,
# Driver/framework centric information.
"db_framework": f"sqlalchemy:{sqlalchemy.__version__}",
}

if self.commenter_options.get("opentelemetry_values", True):
commenter_data.update(**_get_opentelemetry_values())
lzchen marked this conversation as resolved.
Show resolved Hide resolved

# 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)
}
return commenter_data

def _set_db_client_span_attributes(self, span, statement, attrs) -> None:
"""Uses statement and attrs to set attributes of provided Otel span"""
span.set_attribute(SpanAttributes.DB_STATEMENT, statement)
emdneto marked this conversation as resolved.
Show resolved Hide resolved
span.set_attribute(SpanAttributes.DB_SYSTEM, self.vendor)
for key, value in attrs.items():
span.set_attribute(key, value)

def _before_cur_exec(
self, conn, cursor, statement, params, context, _executemany
):
Expand All @@ -233,30 +271,30 @@ def _before_cur_exec(
with trace.use_span(span, end_on_exit=False):
if span.is_recording():
if self.enable_commenter:
commenter_data = {
"db_driver": conn.engine.driver,
# Driver/framework centric information.
"db_framework": f"sqlalchemy:{sqlalchemy.__version__}",
}

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(statement, **commenter_data)

span.set_attribute(SpanAttributes.DB_STATEMENT, statement)
span.set_attribute(SpanAttributes.DB_SYSTEM, self.vendor)
for key, value in attrs.items():
span.set_attribute(key, value)
commenter_data = self._get_commenter_data(conn)

if self.enable_attribute_commenter:
# sqlcomment is added to executed query and db.statement span attribute
statement = _add_sql_comment(
statement, **commenter_data
)
self._set_db_client_span_attributes(
span, statement, attrs
)

else:
# sqlcomment is only added to executed query
# so db.statement is set before add_sql_comment
self._set_db_client_span_attributes(
span, statement, attrs
)
statement = _add_sql_comment(
statement, **commenter_data
)

else:
# no sqlcomment anywhere
self._set_db_client_span_attributes(span, statement, attrs)

lzchen marked this conversation as resolved.
Show resolved Hide resolved
context._otel_span = span

Expand Down
Loading
Loading