diff --git a/CHANGELOG.md b/CHANGELOG.md index cd676c970e..1ff7f48b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3115](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3115)) +### 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 diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py index e1c07096fa..10c2b23a40 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py @@ -126,6 +126,26 @@ :: Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/ +SQLComment in span attribute +**************************** +If sqlcommenter is enabled, you can optionally configure MySQLClient instrumentation to append sqlcomment to query span attribute for convenience of your platform. + +.. code:: python + + from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor + + MySQLClientInstrumentor().instrument( + enable_commenter=True, + enable_attribute_commenter=True, + ) + + +For example, +:: + + Invoking cursor.execute("select * from auth_users") will lead to sql query "select * from auth_users" but when SQLCommenter and attribute_commenter are 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. + API --- """ @@ -159,6 +179,9 @@ def _instrument(self, **kwargs): # pylint: disable=no-self-use tracer_provider = kwargs.get("tracer_provider") enable_sqlcommenter = kwargs.get("enable_commenter", False) commenter_options = kwargs.get("commenter_options", {}) + enable_attribute_commenter = kwargs.get( + "enable_attribute_commenter", False + ) dbapi.wrap_connect( __name__, @@ -170,6 +193,7 @@ def _instrument(self, **kwargs): # pylint: disable=no-self-use tracer_provider=tracer_provider, enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, + enable_attribute_commenter=enable_attribute_commenter, ) def _uninstrument(self, **kwargs): # pylint: disable=no-self-use @@ -182,6 +206,7 @@ def instrument_connection( tracer_provider=None, enable_commenter=None, commenter_options=None, + enable_attribute_commenter=None, ): """Enable instrumentation in a mysqlclient connection. @@ -220,6 +245,7 @@ def instrument_connection( enable_commenter=enable_commenter, commenter_options=commenter_options, connect_module=MySQLdb, + enable_attribute_commenter=enable_attribute_commenter, ) @staticmethod diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py b/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py index ae221f68f4..e2b1e41aa3 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py @@ -19,6 +19,7 @@ import opentelemetry.instrumentation.mysqlclient from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor from opentelemetry.sdk import resources +from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.test.test_base import TestBase @@ -110,12 +111,14 @@ def test_instrument_connection_enable_commenter_dbapi_kwargs( cnx, enable_commenter=True, commenter_options={"foo": True}, + enable_attribute_commenter=True, ) cursor = cnx.cursor() cursor.execute("Select 1;") kwargs = mock_instrument_connection.call_args[1] self.assertEqual(kwargs["enable_commenter"], True) self.assertEqual(kwargs["commenter_options"], {"foo": True}) + self.assertEqual(kwargs["enable_attribute_commenter"], True) def test_instrument_connection_with_dbapi_sqlcomment_enabled(self): mock_connect_module = mock.MagicMock( @@ -150,6 +153,51 @@ def test_instrument_connection_with_dbapi_sqlcomment_enabled(self): mock_cursor.execute.call_args[0][0], 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'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) + + def test_instrument_connection_with_dbapi_sqlcomment_enabled_stmt_enabled( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + cnx_proxy = MySQLClientInstrumentor().instrument_connection( + mock_connection, + enable_commenter=True, + enable_attribute_commenter=True, + ) + cnx_proxy.cursor().execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + 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'*/;", + ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + 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'*/;", + ) def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options( self, @@ -191,6 +239,10 @@ def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options( mock_cursor.execute.call_args[0][0], f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default( self, @@ -221,6 +273,12 @@ def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default( mock_cursor.execute.call_args[0][0], "Select 1;", ) + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) @mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect") @mock.patch("MySQLdb.connect") @@ -233,10 +291,12 @@ def test_instrument_enable_commenter_dbapi_kwargs( MySQLClientInstrumentor()._instrument( enable_commenter=True, commenter_options={"foo": True}, + enable_attribute_commenter=True, ) kwargs = mock_wrap_connect.call_args[1] self.assertEqual(kwargs["enable_commenter"], True) self.assertEqual(kwargs["commenter_options"], {"foo": True}) + self.assertEqual(kwargs["enable_attribute_commenter"], True) def test_instrument_with_dbapi_sqlcomment_enabled( self, @@ -274,6 +334,52 @@ def test_instrument_with_dbapi_sqlcomment_enabled( mock_cursor.execute.call_args[0][0], 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'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) + + def test_instrument_with_dbapi_sqlcomment_enabled_stmt_enabled( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + MySQLClientInstrumentor()._instrument( + enable_commenter=True, + enable_attribute_commenter=True, + ) + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + 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'*/;", + ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + 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'*/;", + ) def test_instrument_with_dbapi_sqlcomment_enabled_with_options( self, @@ -316,6 +422,10 @@ def test_instrument_with_dbapi_sqlcomment_enabled_with_options( mock_cursor.execute.call_args[0][0], f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) def test_instrument_with_dbapi_sqlcomment_not_enabled_default( self, @@ -346,6 +456,12 @@ def test_instrument_with_dbapi_sqlcomment_not_enabled_default( mock_cursor.execute.call_args[0][0], "Select 1;", ) + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) @mock.patch("MySQLdb.connect") # pylint: disable=unused-argument diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py b/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py index 8c608b7655..81390ed48f 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py @@ -80,6 +80,26 @@ :: Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/ +SQLComment in span attribute +**************************** +If sqlcommenter is enabled, you can optionally configure psycopg instrumentation to append sqlcomment to query span attribute for convenience of your platform. + +.. code:: python + + from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor + + PsycopgInstrumentor().instrument( + enable_commenter=True, + enable_attribute_commenter=True, + ) + + +For example, +:: + + Invoking cursor.execute("select * from auth_users") will lead to postgresql query "select * from auth_users" but when SQLCommenter and attribute_commenter are 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. + Usage ----- @@ -159,6 +179,9 @@ def _instrument(self, **kwargs): tracer_provider = kwargs.get("tracer_provider") enable_sqlcommenter = kwargs.get("enable_commenter", False) commenter_options = kwargs.get("commenter_options", {}) + enable_attribute_commenter = kwargs.get( + "enable_attribute_commenter", False + ) dbapi.wrap_connect( __name__, psycopg, @@ -170,6 +193,7 @@ def _instrument(self, **kwargs): db_api_integration_factory=DatabaseApiIntegration, enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, + enable_attribute_commenter=enable_attribute_commenter, ) dbapi.wrap_connect( @@ -183,6 +207,7 @@ def _instrument(self, **kwargs): db_api_integration_factory=DatabaseApiIntegration, enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, + enable_attribute_commenter=enable_attribute_commenter, ) dbapi.wrap_connect( __name__, @@ -195,6 +220,7 @@ def _instrument(self, **kwargs): db_api_integration_factory=DatabaseApiAsyncIntegration, enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, + enable_attribute_commenter=enable_attribute_commenter, ) def _uninstrument(self, **kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py index 0602d3d7ef..9955a1a0a2 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py @@ -80,6 +80,26 @@ :: Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/ +SQLComment in span attribute +**************************** +If sqlcommenter is enabled, you can optionally configure psycopg2 instrumentation to append sqlcomment to query span attribute for convenience of your platform. + +.. code:: python + + from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor + + Psycopg2Instrumentor().instrument( + enable_commenter=True, + enable_attribute_commenter=True, + ) + + +For example, +:: + + Invoking cursor.execute("select * from auth_users") will lead to postgresql query "select * from auth_users" but when SQLCommenter and attribute_commenter are 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. + Usage ----- @@ -156,6 +176,9 @@ def _instrument(self, **kwargs): tracer_provider = kwargs.get("tracer_provider") enable_sqlcommenter = kwargs.get("enable_commenter", False) commenter_options = kwargs.get("commenter_options", {}) + enable_attribute_commenter = kwargs.get( + "enable_attribute_commenter", False + ) dbapi.wrap_connect( __name__, psycopg2, @@ -167,6 +190,7 @@ def _instrument(self, **kwargs): db_api_integration_factory=DatabaseApiIntegration, enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, + enable_attribute_commenter=enable_attribute_commenter, ) def _uninstrument(self, **kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py index 54c614f745..24e5c34062 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py @@ -128,6 +128,26 @@ :: Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/ +SQLComment in span attribute +**************************** +If sqlcommenter is enabled, you can optionally configure PyMySQL instrumentation to append sqlcomment to query span attribute for convenience of your platform. + +.. code:: python + + from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor + + PyMySQLInstrumentor().instrument( + enable_commenter=True, + enable_attribute_commenter=True, + ) + + +For example, +:: + + Invoking cursor.execute("select * from auth_users") will lead to sql query "select * from auth_users" but when SQLCommenter and attribute_commenter are 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. + API --- """ @@ -161,6 +181,9 @@ def _instrument(self, **kwargs): # pylint: disable=no-self-use tracer_provider = kwargs.get("tracer_provider") enable_sqlcommenter = kwargs.get("enable_commenter", False) commenter_options = kwargs.get("commenter_options", {}) + enable_attribute_commenter = kwargs.get( + "enable_attribute_commenter", False + ) dbapi.wrap_connect( __name__, @@ -172,6 +195,7 @@ def _instrument(self, **kwargs): # pylint: disable=no-self-use tracer_provider=tracer_provider, enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, + enable_attribute_commenter=enable_attribute_commenter, ) def _uninstrument(self, **kwargs): # pylint: disable=no-self-use @@ -184,6 +208,7 @@ def instrument_connection( tracer_provider=None, enable_commenter=None, commenter_options=None, + enable_attribute_commenter=None, ): """Enable instrumentation in a PyMySQL connection. @@ -216,6 +241,7 @@ def instrument_connection( enable_commenter=enable_commenter, commenter_options=commenter_options, connect_module=pymysql, + enable_attribute_commenter=enable_attribute_commenter, ) @staticmethod diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py b/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py index 82294236f0..ea59e5df7b 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py +++ b/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py @@ -20,6 +20,7 @@ from opentelemetry import trace as trace_api from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor from opentelemetry.sdk import resources +from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.test.test_base import TestBase @@ -125,12 +126,14 @@ def test_instrument_connection_enable_commenter_dbapi_kwargs( cnx, enable_commenter=True, commenter_options={"foo": True}, + enable_attribute_commenter=True, ) cursor = cnx.cursor() cursor.execute("SELECT * FROM test") kwargs = mock_instrument_connection.call_args[1] self.assertEqual(kwargs["enable_commenter"], True) self.assertEqual(kwargs["commenter_options"], {"foo": True}) + self.assertEqual(kwargs["enable_attribute_commenter"], True) def test_instrument_connection_with_dbapi_sqlcomment_enabled(self): mock_connect_module = mock.MagicMock( @@ -163,6 +166,49 @@ def test_instrument_connection_with_dbapi_sqlcomment_enabled(self): mock_cursor.execute.call_args[0][0], f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) + + def test_instrument_connection_with_dbapi_sqlcomment_enabled_stmt_enabled( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + cnx_proxy = PyMySQLInstrumentor().instrument_connection( + mock_connection, + enable_commenter=True, + enable_attribute_commenter=True, + ) + cnx_proxy.cursor().execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options( self, @@ -202,6 +248,10 @@ def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options( mock_cursor.execute.call_args[0][0], f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default( self, @@ -230,6 +280,12 @@ def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default( mock_cursor.execute.call_args[0][0], "Select 1;", ) + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) @mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect") @mock.patch("pymysql.connect") @@ -242,10 +298,12 @@ def test_instrument_enable_commenter_dbapi_kwargs( PyMySQLInstrumentor()._instrument( enable_commenter=True, commenter_options={"foo": True}, + enable_attribute_commenter=True, ) kwargs = mock_wrap_connect.call_args[1] self.assertEqual(kwargs["enable_commenter"], True) self.assertEqual(kwargs["commenter_options"], {"foo": True}) + self.assertEqual(kwargs["enable_attribute_commenter"], True) def test_instrument_with_dbapi_sqlcomment_enabled( self, @@ -281,6 +339,50 @@ def test_instrument_with_dbapi_sqlcomment_enabled( mock_cursor.execute.call_args[0][0], f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) + + def test_instrument_with_dbapi_sqlcomment_enabled_stmt_enabled( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + PyMySQLInstrumentor()._instrument( + enable_commenter=True, + enable_attribute_commenter=True, + ) + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) def test_instrument_with_dbapi_sqlcomment_enabled_with_options( self, @@ -321,6 +423,10 @@ def test_instrument_with_dbapi_sqlcomment_enabled_with_options( mock_cursor.execute.call_args[0][0], f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", ) + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) def test_instrument_with_dbapi_sqlcomment_not_enabled_default( self, @@ -349,6 +455,12 @@ def test_instrument_with_dbapi_sqlcomment_not_enabled_default( mock_cursor.execute.call_args[0][0], "Select 1;", ) + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], + "Select 1;", + ) @mock.patch("pymysql.connect") # pylint: disable=unused-argument