Skip to content

Commit b3c3aae

Browse files
Merge branch 'main' into mysqlconnector-sqlcomment-v4
2 parents bb1f010 + 1c820ea commit b3c3aae

File tree

6 files changed

+148
-63
lines changed

6 files changed

+148
-63
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
([#2976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2976))
1818
- Add `opentelemetry-instrumentation-openai-v2` to `opentelemetry-bootstrap`
1919
([#2996](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2996))
20+
- `opentelemetry-instrumentation-sqlalchemy` Add sqlcomment to `db.statement` attribute
21+
([#2937](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2937))
22+
- `opentelemetry-instrumentation-dbapi` Add sqlcomment to `db.statement` attribute
23+
([#2935](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2935))
2024
- `opentelemetry-instrumentation-mysql` Add sqlcommenter support
21-
([#2943](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2943))
25+
([#3023](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3023))
2226

2327
### Fixed
2428

instrumentation/opentelemetry-instrumentation-confluent-kafka/README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ Installation
1919
References
2020
----------
2121

22-
* `OpenTelemetry confluent-kafka/ Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/confluent-kafka/confluent-kafka.html>`_
22+
* `OpenTelemetry confluent-kafka/ Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/confluent_kafka/confluent_kafka.html>`_
2323
* `OpenTelemetry Project <https://opentelemetry.io/>`_

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

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

547552

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

Lines changed: 39 additions & 0 deletions
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"

instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -219,28 +219,31 @@ def _before_cur_exec(
219219
)
220220
with trace.use_span(span, end_on_exit=False):
221221
if span.is_recording():
222+
if self.enable_commenter:
223+
commenter_data = {
224+
"db_driver": conn.engine.driver,
225+
# Driver/framework centric information.
226+
"db_framework": f"sqlalchemy:{sqlalchemy.__version__}",
227+
}
228+
229+
if self.commenter_options.get(
230+
"opentelemetry_values", True
231+
):
232+
commenter_data.update(**_get_opentelemetry_values())
233+
234+
# Filter down to just the requested attributes.
235+
commenter_data = {
236+
k: v
237+
for k, v in commenter_data.items()
238+
if self.commenter_options.get(k, True)
239+
}
240+
241+
statement = _add_sql_comment(statement, **commenter_data)
242+
222243
span.set_attribute(SpanAttributes.DB_STATEMENT, statement)
223244
span.set_attribute(SpanAttributes.DB_SYSTEM, self.vendor)
224245
for key, value in attrs.items():
225246
span.set_attribute(key, value)
226-
if self.enable_commenter:
227-
commenter_data = {
228-
"db_driver": conn.engine.driver,
229-
# Driver/framework centric information.
230-
"db_framework": f"sqlalchemy:{sqlalchemy.__version__}",
231-
}
232-
233-
if self.commenter_options.get("opentelemetry_values", True):
234-
commenter_data.update(**_get_opentelemetry_values())
235-
236-
# Filter down to just the requested attributes.
237-
commenter_data = {
238-
k: v
239-
for k, v in commenter_data.items()
240-
if self.commenter_options.get(k, True)
241-
}
242-
243-
statement = _add_sql_comment(statement, **commenter_data)
244247

245248
context._otel_span = span
246249

instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import logging
15+
import re
1516

1617
import pytest
1718
from sqlalchemy import (
@@ -21,6 +22,7 @@
2122

2223
from opentelemetry import context
2324
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
25+
from opentelemetry.semconv.trace import SpanAttributes
2426
from opentelemetry.test.test_base import TestBase
2527

2628

@@ -59,6 +61,38 @@ def test_sqlcommenter_enabled(self):
5961
r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
6062
)
6163

64+
def test_sqlcommenter_enabled_matches_db_statement_attribute(self):
65+
engine = create_engine("sqlite:///:memory:")
66+
SQLAlchemyInstrumentor().instrument(
67+
engine=engine,
68+
tracer_provider=self.tracer_provider,
69+
enable_commenter=True,
70+
commenter_options={"db_framework": False},
71+
)
72+
cnx = engine.connect()
73+
cnx.execute(text("SELECT 1;")).fetchall()
74+
query_log = self.caplog.records[-2].getMessage()
75+
self.assertRegex(
76+
query_log,
77+
r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
78+
)
79+
spans = self.memory_exporter.get_finished_spans()
80+
self.assertEqual(len(spans), 2)
81+
# first span is connection to db
82+
self.assertEqual(spans[0].name, "connect")
83+
# second span is query itself
84+
query_span = spans[1]
85+
self.assertRegex(
86+
query_span.attributes[SpanAttributes.DB_STATEMENT],
87+
r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;",
88+
)
89+
cnx_span_id = re.search(r"[a-zA-Z0-9_]{16}", query_log).group()
90+
db_statement_span_id = re.search(
91+
r"[a-zA-Z0-9_]{16}",
92+
query_span.attributes[SpanAttributes.DB_STATEMENT],
93+
).group()
94+
self.assertEqual(cnx_span_id, db_statement_span_id)
95+
6296
def test_sqlcommenter_enabled_otel_values_false(self):
6397
engine = create_engine("sqlite:///:memory:")
6498
SQLAlchemyInstrumentor().instrument(

0 commit comments

Comments
 (0)