Skip to content

Commit 2efc46c

Browse files
Add mysql-connector sqlcomment support
1 parent 5fd648e commit 2efc46c

File tree

2 files changed

+122
-1
lines changed

2 files changed

+122
-1
lines changed

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

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,75 @@
5050
cursor.close()
5151
instrumented_cnx.close()
5252
53+
SQLCOMMENTER
54+
*****************************************
55+
You can optionally configure mysql-connector instrumentation to enable sqlcommenter which enriches the query with contextual information.
56+
57+
Usage
58+
-----
59+
60+
.. code:: python
61+
62+
import mysql.connector
63+
from opentelemetry.instrumentation.mysql import MySQLInstrumentor
64+
65+
MySQLInstrumentor().instrument(enable_commenter=True, commenter_options={})
66+
67+
cnx = mysql.connector.connect(database="MySQL_Database")
68+
cursor = cnx.cursor()
69+
cursor.execute("INSERT INTO test (testField) VALUES (123)")
70+
cursor.close()
71+
cnx.close()
72+
73+
74+
For example,
75+
::
76+
77+
Invoking cursor.execute("INSERT INTO test (testField) VALUES (123)") will lead to sql query "INSERT INTO test (testField) VALUES (123)" but when SQLCommenter is enabled
78+
the query will get appended with some configurable tags like "INSERT INTO test (testField) VALUES (123) /*tag=value*/;"
79+
80+
**WARNING:** sqlcommenter for mysql-connector instrumentation should NOT be used if your application initializes cursors with ``prepared=True``, which will natively prepare and execute MySQL statements. Adding sqlcommenting will introduce a severe performance penalty by repeating ``Prepare`` of statements by mysql-connector that are made unique by traceparent in sqlcomment. The penalty does not happen if cursor ``prepared=False`` (default) and instrumentor ``enable_commenter=True``.
81+
82+
SQLCommenter Configurations
83+
***************************
84+
We can configure the tags to be appended to the sqlquery log by adding configuration inside commenter_options(default:{}) keyword
85+
86+
db_driver = True(Default) or False
87+
88+
For example,
89+
::
90+
Enabling this flag will add mysql.connector and its version, e.g. /*mysql.connector%%3A1.2.3*/
91+
92+
dbapi_threadsafety = True(Default) or False
93+
94+
For example,
95+
::
96+
Enabling this flag will add threadsafety /*dbapi_threadsafety=2*/
97+
98+
dbapi_level = True(Default) or False
99+
100+
For example,
101+
::
102+
Enabling this flag will add dbapi_level /*dbapi_level='2.0'*/
103+
104+
mysql_client_version = True(Default) or False
105+
106+
For example,
107+
::
108+
Enabling this flag will add mysql_client_version /*mysql_client_version='123'*/
109+
110+
driver_paramstyle = True(Default) or False
111+
112+
For example,
113+
::
114+
Enabling this flag will add driver_paramstyle /*driver_paramstyle='pyformat'*/
115+
116+
opentelemetry_values = True(Default) or False
117+
118+
For example,
119+
::
120+
Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/
121+
53122
API
54123
---
55124
"""
@@ -82,6 +151,8 @@ def _instrument(self, **kwargs):
82151
https://dev.mysql.com/doc/connector-python/en/
83152
"""
84153
tracer_provider = kwargs.get("tracer_provider")
154+
enable_sqlcommenter = kwargs.get("enable_commenter", False)
155+
commenter_options = kwargs.get("commenter_options", {})
85156

86157
dbapi.wrap_connect(
87158
__name__,
@@ -91,14 +162,22 @@ def _instrument(self, **kwargs):
91162
self._CONNECTION_ATTRIBUTES,
92163
version=__version__,
93164
tracer_provider=tracer_provider,
165+
enable_commenter=enable_sqlcommenter,
166+
commenter_options=commenter_options,
94167
)
95168

96169
def _uninstrument(self, **kwargs):
97170
""" "Disable MySQL instrumentation"""
98171
dbapi.unwrap_connect(mysql.connector, "connect")
99172

100173
# pylint:disable=no-self-use
101-
def instrument_connection(self, connection, tracer_provider=None):
174+
def instrument_connection(
175+
self,
176+
connection,
177+
tracer_provider=None,
178+
enable_commenter=None,
179+
commenter_options=None,
180+
):
102181
"""Enable instrumentation in a MySQL connection.
103182
104183
Args:
@@ -109,6 +188,10 @@ def instrument_connection(self, connection, tracer_provider=None):
109188
tracer_provider:
110189
An optional `TracerProvider` instance to use for tracing. If not provided, the globally
111190
configured tracer provider will be automatically used.
191+
enable_commenter:
192+
Optional flag to enable/disable sqlcommenter (default False).
193+
commenter_options:
194+
Optional configurations for tags to be appended at the sql query.
112195
113196
Returns:
114197
An instrumented MySQL connection with OpenTelemetry tracing enabled.
@@ -120,6 +203,8 @@ def instrument_connection(self, connection, tracer_provider=None):
120203
self._CONNECTION_ATTRIBUTES,
121204
version=__version__,
122205
tracer_provider=tracer_provider,
206+
enable_commenter=enable_commenter,
207+
commenter_options=commenter_options,
123208
)
124209

125210
def uninstrument_connection(self, connection):

instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,42 @@ def test_instrument_connection_no_op_tracer_provider(self, mock_connect):
102102
spans_list = self.memory_exporter.get_finished_spans()
103103
self.assertEqual(len(spans_list), 0)
104104

105+
@mock.patch("opentelemetry.instrumentation.dbapi.instrument_connection")
106+
@mock.patch("mysql.connector.connect")
107+
# pylint: disable=unused-argument
108+
def test_instrument_connection_enable_commenter(
109+
self,
110+
mock_connect,
111+
mock_instrument_connection,
112+
):
113+
cnx, query = connect_and_execute_query()
114+
cnx = MySQLInstrumentor().instrument_connection(
115+
cnx,
116+
enable_commenter=True,
117+
commenter_options={"foo": True},
118+
)
119+
cursor = cnx.cursor()
120+
cursor.execute(query)
121+
kwargs = mock_instrument_connection.call_args[1]
122+
self.assertEqual(kwargs["enable_commenter"], True)
123+
self.assertEqual(kwargs["commenter_options"], {"foo": True})
124+
125+
@mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect")
126+
@mock.patch("mysql.connector.connect")
127+
# pylint: disable=unused-argument
128+
def test__instrument_enable_commenter(
129+
self,
130+
mock_connect,
131+
mock_wrap_connect,
132+
):
133+
MySQLInstrumentor()._instrument(
134+
enable_commenter=True,
135+
commenter_options={"foo": True},
136+
)
137+
kwargs = mock_wrap_connect.call_args[1]
138+
self.assertEqual(kwargs["enable_commenter"], True)
139+
self.assertEqual(kwargs["commenter_options"], {"foo": True})
140+
105141
@mock.patch("mysql.connector.connect")
106142
# pylint: disable=unused-argument
107143
def test_uninstrument_connection(self, mock_connect):

0 commit comments

Comments
 (0)