Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3615](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3615))
- `opentelemetry-instrumentation-fastapi`: Don't pass bounded server_request_hook when using `FastAPIInstrumentor.instrument()`
([#3701](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3701))
- `opentelemetry-instrumentation-dbapi`: Fix sqlcomment calculation of mysql_client_version field if connection reassignment, with "unknown" fallback
([#3729](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3729))

### Added

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,9 +478,24 @@ def _capture_mysql_version(self, cursor) -> None:
"mysql_client_version"
]
):
self._db_api_integration.commenter_data["mysql_client_version"] = (
cursor._cnx._cmysql.get_client_info()
)
try:
# Autoinstrumentation and some programmatic calls
self._db_api_integration.commenter_data[
"mysql_client_version"
] = cursor._cnx._cmysql.get_client_info()
except AttributeError:
# Other programmatic instrumentation with reassigned wrapped connection
try:
self._db_api_integration.commenter_data[
"mysql_client_version"
] = cursor._connection._cmysql.get_client_info()
except AttributeError as exc:
_logger.error(
"Could not set mysql_client_version: %s", exc
)
self._db_api_integration.commenter_data[
"mysql_client_version"
] = "unknown"

def _get_commenter_data(self) -> dict:
"""Uses DB-API integration to return commenter data for sqlcomment"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,89 @@ def test_non_string_sql_conversion(self):
spans_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans_list), 1)

def test_capture_mysql_version_primary_success(self):
connect_module = mock.MagicMock()
connect_module.__name__ = "mysql.connector"
connect_module.__version__ = "2.2.9"
db_integration = dbapi.DatabaseApiIntegration(
"instrumenting_module_test_name",
"mysql",
enable_commenter=True,
connect_module=connect_module,
)
mock_cursor = mock.MagicMock()
mock_cursor._cnx._cmysql.get_client_info.return_value = "8.0.32"
mock_connection = db_integration.wrapped_connection(
mock_connect, {}, {}
)
cursor = mock_connection.cursor()
cursor._cnx = mock_cursor._cnx
cursor.execute("SELECT 1;")
mock_cursor._cnx._cmysql.get_client_info.assert_called_once()
self.assertEqual(
db_integration.commenter_data["mysql_client_version"], "8.0.32"
)

def test_capture_mysql_version_fallback_success(self):
connect_module = mock.MagicMock()
connect_module.__name__ = "mysql.connector"
connect_module.__version__ = "2.2.9"
db_integration = dbapi.DatabaseApiIntegration(
"instrumenting_module_test_name",
"mysql",
enable_commenter=True,
connect_module=connect_module,
)
mock_cursor = mock.MagicMock()
mock_cursor._cnx._cmysql.get_client_info.side_effect = AttributeError(
"Primary method failed"
)
mock_cursor._connection._cmysql.get_client_info.return_value = "8.0.33"
mock_connection = db_integration.wrapped_connection(
mock_connect, {}, {}
)
cursor = mock_connection.cursor()
cursor._cnx = mock_cursor._cnx
cursor._connection = mock_cursor._connection
cursor.execute("SELECT 1;")
mock_cursor._cnx._cmysql.get_client_info.assert_called_once()
mock_cursor._connection._cmysql.get_client_info.assert_called_once()
self.assertEqual(
db_integration.commenter_data["mysql_client_version"], "8.0.33"
)

@mock.patch("opentelemetry.instrumentation.dbapi._logger")
def test_capture_mysql_version_fallback(self, mock_logger):
connect_module = mock.MagicMock()
connect_module.__name__ = "mysql.connector"
connect_module.__version__ = "2.2.9"
db_integration = dbapi.DatabaseApiIntegration(
"instrumenting_module_test_name",
"mysql",
enable_commenter=True,
connect_module=connect_module,
)
mock_cursor = mock.MagicMock()
mock_cursor._cnx._cmysql.get_client_info.side_effect = AttributeError(
"Primary method failed"
)
mock_cursor._connection._cmysql.get_client_info.side_effect = (
AttributeError("Fallback method failed")
)
mock_connection = db_integration.wrapped_connection(
mock_connect, {}, {}
)
cursor = mock_connection.cursor()
cursor._cnx = mock_cursor._cnx
cursor._connection = mock_cursor._connection
cursor.execute("SELECT 1;")
mock_cursor._cnx._cmysql.get_client_info.assert_called_once()
mock_cursor._connection._cmysql.get_client_info.assert_called_once()
mock_logger.error.assert_called_once()
self.assertEqual(
db_integration.commenter_data["mysql_client_version"], "unknown"
)


# pylint: disable=unused-argument
def mock_connect(*args, **kwargs):
Expand Down