From b100c4b66b185bea4e123fca593adae5b1f1971b Mon Sep 17 00:00:00 2001 From: henry3260 Date: Sat, 18 Jul 2026 11:07:40 +0800 Subject: [PATCH 1/2] opentelemetry-instrumentation-mysqlclient: add semconv stability opt-in test coverage Cover database,http and database/dup,http/dup opt-in modes, verifying stable-only and dual-emission span attributes, mirroring the pattern from #4810. --- .../tests/test_mysqlclient_integration.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py b/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py index a6dd9e7ebe..4157fb12e6 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py @@ -1,19 +1,72 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 +import contextlib from unittest import mock import MySQLdb import opentelemetry.instrumentation.mysqlclient +from opentelemetry.instrumentation._semconv import ( + OTEL_SEMCONV_STABILITY_OPT_IN, + _OpenTelemetrySemanticConventionStability, +) from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor from opentelemetry.sdk import resources from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, DB_STATEMENT, + DB_SYSTEM, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) +from opentelemetry.semconv.attributes.db_attributes import ( + DB_NAMESPACE, + DB_QUERY_TEXT, + DB_SYSTEM_NAME, +) +from opentelemetry.semconv.attributes.server_attributes import ( + SERVER_ADDRESS, + SERVER_PORT, ) from opentelemetry.test.test_base import TestBase +@contextlib.contextmanager +def use_semconv_opt_in(sem_conv_mode): + env_patch = mock.patch.dict( + "os.environ", + {OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode}, + ) + _OpenTelemetrySemanticConventionStability._initialized = False + env_patch.start() + try: + yield + finally: + env_patch.stop() + _OpenTelemetrySemanticConventionStability._initialized = False + + +def connect_and_execute_query(): + cnx = MySQLdb.connect(database="test") + cursor = cnx.cursor() + query = "SELECT * FROM test" + cursor.execute(query) + return cnx, query + + +def make_mysqlclient_connection_mock(): + cnx = mock.MagicMock() + cnx.db = "test" + cnx.host = "localhost" + cnx.port = 3306 + cnx.user = "testuser" + return cnx + + class TestMySQLClientIntegration(TestBase): # pylint: disable=invalid-name def tearDown(self): @@ -504,3 +557,59 @@ def test_uninstrument_connection(self, mock_connect): spans_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans_list), 0) + + @mock.patch("MySQLdb.connect") + def test_semconv_stable(self, mock_connect): + """database,http opt-in emits only stable attributes.""" + with use_semconv_opt_in("database,http"): + mock_connect.return_value = make_mysqlclient_connection_mock() + MySQLClientInstrumentor().instrument() + + connect_and_execute_query() + + spans_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans_list), 1) + span = spans_list[0] + + self.assertEqual(span.attributes[DB_SYSTEM_NAME], "mysql") + self.assertEqual(span.attributes[DB_NAMESPACE], "test") + self.assertEqual( + span.attributes[DB_QUERY_TEXT], "SELECT * FROM test" + ) + self.assertEqual(span.attributes[SERVER_ADDRESS], "localhost") + self.assertEqual(span.attributes[SERVER_PORT], 3306) + self.assertNotIn(DB_SYSTEM, span.attributes) + self.assertNotIn(DB_NAME, span.attributes) + self.assertNotIn(DB_STATEMENT, span.attributes) + self.assertNotIn(DB_USER, span.attributes) + self.assertNotIn(NET_PEER_NAME, span.attributes) + self.assertNotIn(NET_PEER_PORT, span.attributes) + + @mock.patch("MySQLdb.connect") + def test_semconv_dup(self, mock_connect): + """database/dup,http/dup opt-in emits both legacy and stable attributes.""" + with use_semconv_opt_in("database/dup,http/dup"): + mock_connect.return_value = make_mysqlclient_connection_mock() + MySQLClientInstrumentor().instrument() + + connect_and_execute_query() + + spans_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans_list), 1) + span = spans_list[0] + + self.assertEqual(span.attributes[DB_SYSTEM], "mysql") + self.assertEqual(span.attributes[DB_SYSTEM_NAME], "mysql") + self.assertEqual(span.attributes[DB_NAME], "test") + self.assertEqual(span.attributes[DB_NAMESPACE], "test") + self.assertEqual( + span.attributes[DB_STATEMENT], "SELECT * FROM test" + ) + self.assertEqual( + span.attributes[DB_QUERY_TEXT], "SELECT * FROM test" + ) + self.assertEqual(span.attributes[DB_USER], "testuser") + self.assertEqual(span.attributes[NET_PEER_NAME], "localhost") + self.assertEqual(span.attributes[NET_PEER_PORT], 3306) + self.assertEqual(span.attributes[SERVER_ADDRESS], "localhost") + self.assertEqual(span.attributes[SERVER_PORT], 3306) From d25b62bc5f50a46910a999116a99f013934ce2df Mon Sep 17 00:00:00 2001 From: henry3260 Date: Sat, 18 Jul 2026 13:34:48 +0800 Subject: [PATCH 2/2] Add changelog fragment for semconv opt-in mode tests --- .changelog/4834.added | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/4834.added diff --git a/.changelog/4834.added b/.changelog/4834.added new file mode 100644 index 0000000000..cdf619b6dc --- /dev/null +++ b/.changelog/4834.added @@ -0,0 +1 @@ +`opentelemetry-instrumentation-mysqlclient`: add semantic convention stability opt-in mode tests