Skip to content

Commit 81640bb

Browse files
psycopg(2), mysqlclient, pymysql support enable_attribute_commenter
1 parent 1a515a1 commit 81640bb

File tree

6 files changed

+330
-0
lines changed

6 files changed

+330
-0
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,26 @@
102102
::
103103
Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/
104104
105+
SQLComment in span attribute
106+
****************************
107+
If sqlcommenter is enabled, you can optionally configure MySQLClient instrumentation to append sqlcomment to query span attribute for convenience of your platform.
108+
109+
.. code:: python
110+
111+
from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor
112+
113+
MySQLClientInstrumentor().instrument(
114+
enable_commenter=True,
115+
enable_attribute_commenter=True,
116+
)
117+
118+
119+
For example,
120+
::
121+
122+
Invoking cursor.execute("select * from auth_users") will lead to sql query "select * from auth_users" but when SQLCommenter and attribute_commenter are enabled
123+
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.
124+
105125
API
106126
---
107127
"""
@@ -135,6 +155,9 @@ def _instrument(self, **kwargs): # pylint: disable=no-self-use
135155
tracer_provider = kwargs.get("tracer_provider")
136156
enable_sqlcommenter = kwargs.get("enable_commenter", False)
137157
commenter_options = kwargs.get("commenter_options", {})
158+
enable_attribute_commenter = kwargs.get(
159+
"enable_attribute_commenter", False
160+
)
138161

139162
dbapi.wrap_connect(
140163
__name__,
@@ -146,6 +169,7 @@ def _instrument(self, **kwargs): # pylint: disable=no-self-use
146169
tracer_provider=tracer_provider,
147170
enable_commenter=enable_sqlcommenter,
148171
commenter_options=commenter_options,
172+
enable_attribute_commenter=enable_attribute_commenter,
149173
)
150174

151175
def _uninstrument(self, **kwargs): # pylint: disable=no-self-use
@@ -158,6 +182,7 @@ def instrument_connection(
158182
tracer_provider=None,
159183
enable_commenter=None,
160184
commenter_options=None,
185+
enable_attribute_commenter=None,
161186
):
162187
"""Enable instrumentation in a mysqlclient connection.
163188
@@ -180,6 +205,7 @@ def instrument_connection(
180205
enable_commenter=enable_commenter,
181206
commenter_options=commenter_options,
182207
connect_module=MySQLdb,
208+
enable_attribute_commenter=enable_attribute_commenter,
183209
)
184210

185211
@staticmethod

instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import opentelemetry.instrumentation.mysqlclient
2020
from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor
2121
from opentelemetry.sdk import resources
22+
from opentelemetry.semconv.trace import SpanAttributes
2223
from opentelemetry.test.test_base import TestBase
2324

2425

@@ -110,12 +111,14 @@ def test_instrument_connection_enable_commenter_dbapi_kwargs(
110111
cnx,
111112
enable_commenter=True,
112113
commenter_options={"foo": True},
114+
enable_attribute_commenter=True,
113115
)
114116
cursor = cnx.cursor()
115117
cursor.execute("Select 1;")
116118
kwargs = mock_instrument_connection.call_args[1]
117119
self.assertEqual(kwargs["enable_commenter"], True)
118120
self.assertEqual(kwargs["commenter_options"], {"foo": True})
121+
self.assertEqual(kwargs["enable_attribute_commenter"], True)
119122

120123
def test_instrument_connection_with_dbapi_sqlcomment_enabled(self):
121124
mock_connect_module = mock.MagicMock(
@@ -150,6 +153,51 @@ def test_instrument_connection_with_dbapi_sqlcomment_enabled(self):
150153
mock_cursor.execute.call_args[0][0],
151154
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
152155
)
156+
self.assertEqual(
157+
span.attributes[SpanAttributes.DB_STATEMENT],
158+
"Select 1;",
159+
)
160+
161+
def test_instrument_connection_with_dbapi_sqlcomment_enabled_stmt_enabled(
162+
self,
163+
):
164+
mock_connect_module = mock.MagicMock(
165+
__name__="MySQLdb",
166+
threadsafety="123",
167+
apilevel="123",
168+
paramstyle="test",
169+
)
170+
mock_connect_module._mysql.get_client_info.return_value = "foobaz"
171+
mock_cursor = mock_connect_module.connect().cursor()
172+
mock_connection = mock.MagicMock()
173+
mock_connection.cursor.return_value = mock_cursor
174+
175+
with mock.patch(
176+
"opentelemetry.instrumentation.mysqlclient.MySQLdb",
177+
mock_connect_module,
178+
), mock.patch(
179+
"opentelemetry.instrumentation.dbapi.util_version",
180+
return_value="foobar",
181+
):
182+
cnx_proxy = MySQLClientInstrumentor().instrument_connection(
183+
mock_connection,
184+
enable_commenter=True,
185+
enable_attribute_commenter=True,
186+
)
187+
cnx_proxy.cursor().execute("Select 1;")
188+
189+
spans_list = self.memory_exporter.get_finished_spans()
190+
span = spans_list[0]
191+
span_id = format(span.get_span_context().span_id, "016x")
192+
trace_id = format(span.get_span_context().trace_id, "032x")
193+
self.assertEqual(
194+
mock_cursor.execute.call_args[0][0],
195+
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
196+
)
197+
self.assertEqual(
198+
span.attributes[SpanAttributes.DB_STATEMENT],
199+
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
200+
)
153201

154202
def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options(
155203
self,
@@ -191,6 +239,10 @@ def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options(
191239
mock_cursor.execute.call_args[0][0],
192240
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
193241
)
242+
self.assertEqual(
243+
span.attributes[SpanAttributes.DB_STATEMENT],
244+
"Select 1;",
245+
)
194246

195247
def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default(
196248
self,
@@ -221,6 +273,12 @@ def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default(
221273
mock_cursor.execute.call_args[0][0],
222274
"Select 1;",
223275
)
276+
spans_list = self.memory_exporter.get_finished_spans()
277+
span = spans_list[0]
278+
self.assertEqual(
279+
span.attributes[SpanAttributes.DB_STATEMENT],
280+
"Select 1;",
281+
)
224282

225283
@mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect")
226284
@mock.patch("MySQLdb.connect")
@@ -233,10 +291,12 @@ def test_instrument_enable_commenter_dbapi_kwargs(
233291
MySQLClientInstrumentor()._instrument(
234292
enable_commenter=True,
235293
commenter_options={"foo": True},
294+
enable_attribute_commenter=True,
236295
)
237296
kwargs = mock_wrap_connect.call_args[1]
238297
self.assertEqual(kwargs["enable_commenter"], True)
239298
self.assertEqual(kwargs["commenter_options"], {"foo": True})
299+
self.assertEqual(kwargs["enable_attribute_commenter"], True)
240300

241301
def test_instrument_with_dbapi_sqlcomment_enabled(
242302
self,
@@ -274,6 +334,52 @@ def test_instrument_with_dbapi_sqlcomment_enabled(
274334
mock_cursor.execute.call_args[0][0],
275335
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
276336
)
337+
self.assertEqual(
338+
span.attributes[SpanAttributes.DB_STATEMENT],
339+
"Select 1;",
340+
)
341+
342+
def test_instrument_with_dbapi_sqlcomment_enabled_stmt_enabled(
343+
self,
344+
):
345+
mock_connect_module = mock.MagicMock(
346+
__name__="MySQLdb",
347+
threadsafety="123",
348+
apilevel="123",
349+
paramstyle="test",
350+
)
351+
mock_connect_module._mysql.get_client_info.return_value = "foobaz"
352+
mock_cursor = mock_connect_module.connect().cursor()
353+
mock_connection = mock.MagicMock()
354+
mock_connection.cursor.return_value = mock_cursor
355+
356+
with mock.patch(
357+
"opentelemetry.instrumentation.mysqlclient.MySQLdb",
358+
mock_connect_module,
359+
), mock.patch(
360+
"opentelemetry.instrumentation.dbapi.util_version",
361+
return_value="foobar",
362+
):
363+
MySQLClientInstrumentor()._instrument(
364+
enable_commenter=True,
365+
enable_attribute_commenter=True,
366+
)
367+
cnx = mock_connect_module.connect(database="test")
368+
cursor = cnx.cursor()
369+
cursor.execute("Select 1;")
370+
371+
spans_list = self.memory_exporter.get_finished_spans()
372+
span = spans_list[0]
373+
span_id = format(span.get_span_context().span_id, "016x")
374+
trace_id = format(span.get_span_context().trace_id, "032x")
375+
self.assertEqual(
376+
mock_cursor.execute.call_args[0][0],
377+
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
378+
)
379+
self.assertEqual(
380+
span.attributes[SpanAttributes.DB_STATEMENT],
381+
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
382+
)
277383

278384
def test_instrument_with_dbapi_sqlcomment_enabled_with_options(
279385
self,
@@ -316,6 +422,10 @@ def test_instrument_with_dbapi_sqlcomment_enabled_with_options(
316422
mock_cursor.execute.call_args[0][0],
317423
f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;",
318424
)
425+
self.assertEqual(
426+
span.attributes[SpanAttributes.DB_STATEMENT],
427+
"Select 1;",
428+
)
319429

320430
def test_instrument_with_dbapi_sqlcomment_not_enabled_default(
321431
self,
@@ -346,6 +456,12 @@ def test_instrument_with_dbapi_sqlcomment_not_enabled_default(
346456
mock_cursor.execute.call_args[0][0],
347457
"Select 1;",
348458
)
459+
spans_list = self.memory_exporter.get_finished_spans()
460+
span = spans_list[0]
461+
self.assertEqual(
462+
span.attributes[SpanAttributes.DB_STATEMENT],
463+
"Select 1;",
464+
)
349465

350466
@mock.patch("MySQLdb.connect")
351467
# pylint: disable=unused-argument

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@
8080
::
8181
Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/
8282
83+
SQLComment in span attribute
84+
****************************
85+
If sqlcommenter is enabled, you can optionally configure psycopg instrumentation to append sqlcomment to query span attribute for convenience of your platform.
86+
87+
.. code:: python
88+
89+
from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor
90+
91+
PsycopgInstrumentor().instrument(
92+
enable_commenter=True,
93+
enable_attribute_commenter=True,
94+
)
95+
96+
97+
For example,
98+
::
99+
100+
Invoking cursor.execute("select * from auth_users") will lead to postgresql query "select * from auth_users" but when SQLCommenter and attribute_commenter are enabled
101+
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.
102+
83103
Usage
84104
-----
85105
@@ -143,6 +163,9 @@ def _instrument(self, **kwargs):
143163
tracer_provider = kwargs.get("tracer_provider")
144164
enable_sqlcommenter = kwargs.get("enable_commenter", False)
145165
commenter_options = kwargs.get("commenter_options", {})
166+
enable_attribute_commenter = kwargs.get(
167+
"enable_attribute_commenter", False
168+
)
146169
dbapi.wrap_connect(
147170
__name__,
148171
psycopg,
@@ -154,6 +177,7 @@ def _instrument(self, **kwargs):
154177
db_api_integration_factory=DatabaseApiIntegration,
155178
enable_commenter=enable_sqlcommenter,
156179
commenter_options=commenter_options,
180+
enable_attribute_commenter=enable_attribute_commenter,
157181
)
158182

159183
dbapi.wrap_connect(
@@ -167,6 +191,7 @@ def _instrument(self, **kwargs):
167191
db_api_integration_factory=DatabaseApiIntegration,
168192
enable_commenter=enable_sqlcommenter,
169193
commenter_options=commenter_options,
194+
enable_attribute_commenter=enable_attribute_commenter,
170195
)
171196
dbapi.wrap_connect(
172197
__name__,
@@ -179,6 +204,7 @@ def _instrument(self, **kwargs):
179204
db_api_integration_factory=DatabaseApiAsyncIntegration,
180205
enable_commenter=enable_sqlcommenter,
181206
commenter_options=commenter_options,
207+
enable_attribute_commenter=enable_attribute_commenter,
182208
)
183209

184210
def _uninstrument(self, **kwargs):

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@
8080
::
8181
Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/
8282
83+
SQLComment in span attribute
84+
****************************
85+
If sqlcommenter is enabled, you can optionally configure psycopg2 instrumentation to append sqlcomment to query span attribute for convenience of your platform.
86+
87+
.. code:: python
88+
89+
from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor
90+
91+
Psycopg2Instrumentor().instrument(
92+
enable_commenter=True,
93+
enable_attribute_commenter=True,
94+
)
95+
96+
97+
For example,
98+
::
99+
100+
Invoking cursor.execute("select * from auth_users") will lead to postgresql query "select * from auth_users" but when SQLCommenter and attribute_commenter are enabled
101+
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.
102+
83103
Usage
84104
-----
85105
@@ -140,6 +160,9 @@ def _instrument(self, **kwargs):
140160
tracer_provider = kwargs.get("tracer_provider")
141161
enable_sqlcommenter = kwargs.get("enable_commenter", False)
142162
commenter_options = kwargs.get("commenter_options", {})
163+
enable_attribute_commenter = kwargs.get(
164+
"enable_attribute_commenter", False
165+
)
143166
dbapi.wrap_connect(
144167
__name__,
145168
psycopg2,
@@ -151,6 +174,7 @@ def _instrument(self, **kwargs):
151174
db_api_integration_factory=DatabaseApiIntegration,
152175
enable_commenter=enable_sqlcommenter,
153176
commenter_options=commenter_options,
177+
enable_attribute_commenter=enable_attribute_commenter,
154178
)
155179

156180
def _uninstrument(self, **kwargs):

0 commit comments

Comments
 (0)