From c4ac4eeda9facbab5dd734c53081bd7c9ae4f960 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Wed, 15 Apr 2026 10:56:35 -0400 Subject: [PATCH 1/4] feat: Add `db.driver.name` spans to database integrations Adds `db.driver.name` spans to the following integrations: - asyncpg - clickhouse_driver - pymongo - redis - sqlalchemy Adding this attribute for database queries made using the Django integration will be done as part of PY-2377 / #6081 because it's less straightforward to get the underlying database driver used. Fixes PY-2376 Fixes #6080 --- sentry_sdk/consts.py | 6 ++++++ sentry_sdk/integrations/asyncpg.py | 3 +++ sentry_sdk/integrations/clickhouse_driver.py | 1 + sentry_sdk/integrations/pymongo.py | 2 ++ .../integrations/redis/modules/queries.py | 1 + sentry_sdk/integrations/sqlalchemy.py | 7 +++++++ tests/integrations/asyncpg/test_asyncpg.py | 2 ++ .../test_clickhouse_driver.py | 20 +++++++++++++++++++ tests/integrations/pymongo/test_pymongo.py | 3 +++ tests/integrations/redis/test_redis.py | 2 ++ .../sqlalchemy/test_sqlalchemy.py | 2 ++ 11 files changed, 49 insertions(+) diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index 73e5a6d9cb..25a50a12b2 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -441,6 +441,12 @@ class SPANDATA: Example: myDatabase """ + DB_DRIVER_NAME = "db.driver.name" + """ + The name of the database driver being used for the connection. + Example: "psycopg2" + """ + DB_OPERATION = "db.operation" """ The name of the operation being executed, e.g. the MongoDB command name such as findAndModify, or the SQL keyword. diff --git a/sentry_sdk/integrations/asyncpg.py b/sentry_sdk/integrations/asyncpg.py index d1cf6fcc92..09e283c5c3 100644 --- a/sentry_sdk/integrations/asyncpg.py +++ b/sentry_sdk/integrations/asyncpg.py @@ -184,6 +184,7 @@ async def _inner(*args: "Any", **kwargs: "Any") -> "T": pass span.set_data(SPANDATA.DB_NAME, database) span.set_data(SPANDATA.DB_USER, user) + span.set_data(SPANDATA.DB_DRIVER_NAME, "asyncpg") with capture_internal_exceptions(): sentry_sdk.add_breadcrumb( @@ -214,3 +215,5 @@ def _set_db_data(span: "Span", conn: "Any") -> None: user = conn._params.user if user: span.set_data(SPANDATA.DB_USER, user) + + span.set_data(SPANDATA.DB_DRIVER_NAME, "asyncpg") diff --git a/sentry_sdk/integrations/clickhouse_driver.py b/sentry_sdk/integrations/clickhouse_driver.py index 7bbea94210..3b7ec9891e 100644 --- a/sentry_sdk/integrations/clickhouse_driver.py +++ b/sentry_sdk/integrations/clickhouse_driver.py @@ -165,6 +165,7 @@ def wrapped_generator() -> "Iterator[Any]": def _set_db_data(span: "Span", connection: "Connection") -> None: span.set_data(SPANDATA.DB_SYSTEM, "clickhouse") + span.set_data(SPANDATA.DB_DRIVER_NAME, "clickhouse-driver") span.set_data(SPANDATA.SERVER_ADDRESS, connection.host) span.set_data(SPANDATA.SERVER_PORT, connection.port) span.set_data(SPANDATA.DB_NAME, connection.database) diff --git a/sentry_sdk/integrations/pymongo.py b/sentry_sdk/integrations/pymongo.py index 86399b54d1..59daab34da 100644 --- a/sentry_sdk/integrations/pymongo.py +++ b/sentry_sdk/integrations/pymongo.py @@ -88,6 +88,7 @@ def _get_db_data(event: "Any") -> "Dict[str, Any]": data = {} data[SPANDATA.DB_SYSTEM] = "mongodb" + data[SPANDATA.DB_DRIVER_NAME] = "pymongo" db_name = event.database_name if db_name is not None: @@ -128,6 +129,7 @@ def started(self, event: "CommandStartedEvent") -> None: tags = { "db.name": event.database_name, SPANDATA.DB_SYSTEM: "mongodb", + SPANDATA.DB_DRIVER_NAME: "pymongo", SPANDATA.DB_OPERATION: event.command_name, SPANDATA.DB_MONGODB_COLLECTION: command.get(event.command_name), } diff --git a/sentry_sdk/integrations/redis/modules/queries.py b/sentry_sdk/integrations/redis/modules/queries.py index 3e8a820f44..4712dc17c5 100644 --- a/sentry_sdk/integrations/redis/modules/queries.py +++ b/sentry_sdk/integrations/redis/modules/queries.py @@ -44,6 +44,7 @@ def _get_db_span_description( def _set_db_data_on_span(span: "Span", connection_params: "dict[str, Any]") -> None: span.set_data(SPANDATA.DB_SYSTEM, "redis") + span.set_data(SPANDATA.DB_DRIVER_NAME, "redis") db = connection_params.get("db") if db is not None: diff --git a/sentry_sdk/integrations/sqlalchemy.py b/sentry_sdk/integrations/sqlalchemy.py index 7d3ed95373..457cd4a428 100644 --- a/sentry_sdk/integrations/sqlalchemy.py +++ b/sentry_sdk/integrations/sqlalchemy.py @@ -137,6 +137,13 @@ def _set_db_data(span: "Span", conn: "Any") -> None: if db_system is not None: span.set_data(SPANDATA.DB_SYSTEM, db_system) + try: + driver = conn.engine.driver + if driver: + span.set_data(SPANDATA.DB_DRIVER_NAME, driver) + except Exception: + pass + if conn.engine.url is None: return diff --git a/tests/integrations/asyncpg/test_asyncpg.py b/tests/integrations/asyncpg/test_asyncpg.py index 62b5f166f7..a499fcc2ec 100644 --- a/tests/integrations/asyncpg/test_asyncpg.py +++ b/tests/integrations/asyncpg/test_asyncpg.py @@ -49,6 +49,7 @@ def _get_db_name(): "db.name": PG_NAME, "db.system": "postgresql", "db.user": PG_USER, + "db.driver.name": "asyncpg", "server.address": PG_HOST, "server.port": PG_PORT, } @@ -553,6 +554,7 @@ async def test_query_source_enabled( assert SPANDATA.CODE_NAMESPACE in data assert SPANDATA.CODE_FILEPATH in data assert SPANDATA.CODE_FUNCTION in data + assert data.get(SPANDATA.DB_DRIVER_NAME) == "asyncpg" @pytest.mark.asyncio diff --git a/tests/integrations/clickhouse_driver/test_clickhouse_driver.py b/tests/integrations/clickhouse_driver/test_clickhouse_driver.py index 635f9334c4..b501aa3531 100644 --- a/tests/integrations/clickhouse_driver/test_clickhouse_driver.py +++ b/tests/integrations/clickhouse_driver/test_clickhouse_driver.py @@ -42,6 +42,7 @@ def test_clickhouse_client_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -54,6 +55,7 @@ def test_clickhouse_client_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -66,6 +68,7 @@ def test_clickhouse_client_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -78,6 +81,7 @@ def test_clickhouse_client_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -90,6 +94,7 @@ def test_clickhouse_client_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -257,6 +262,7 @@ def test_clickhouse_client_spans( "description": "DROP TABLE IF EXISTS test", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -272,6 +278,7 @@ def test_clickhouse_client_spans( "description": "CREATE TABLE test (x Int32) ENGINE = Memory", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -287,6 +294,7 @@ def test_clickhouse_client_spans( "description": "INSERT INTO test (x) VALUES", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -302,6 +310,7 @@ def test_clickhouse_client_spans( "description": "INSERT INTO test (x) VALUES", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -317,6 +326,7 @@ def test_clickhouse_client_spans( "description": "SELECT sum(x) FROM test WHERE x > 150", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -529,6 +539,7 @@ def test_clickhouse_dbapi_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -541,6 +552,7 @@ def test_clickhouse_dbapi_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -553,6 +565,7 @@ def test_clickhouse_dbapi_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -565,6 +578,7 @@ def test_clickhouse_dbapi_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -577,6 +591,7 @@ def test_clickhouse_dbapi_breadcrumbs(sentry_init, capture_events) -> None: "category": "query", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -737,6 +752,7 @@ def test_clickhouse_dbapi_spans(sentry_init, capture_events, capture_envelopes) "description": "DROP TABLE IF EXISTS test", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -752,6 +768,7 @@ def test_clickhouse_dbapi_spans(sentry_init, capture_events, capture_envelopes) "description": "CREATE TABLE test (x Int32) ENGINE = Memory", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -767,6 +784,7 @@ def test_clickhouse_dbapi_spans(sentry_init, capture_events, capture_envelopes) "description": "INSERT INTO test (x) VALUES", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -782,6 +800,7 @@ def test_clickhouse_dbapi_spans(sentry_init, capture_events, capture_envelopes) "description": "INSERT INTO test (x) VALUES", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", @@ -797,6 +816,7 @@ def test_clickhouse_dbapi_spans(sentry_init, capture_events, capture_envelopes) "description": "SELECT sum(x) FROM test WHERE x > 150", "data": { "db.system": "clickhouse", + "db.driver.name": "clickhouse-driver", "db.name": "", "db.user": "default", "server.address": "localhost", diff --git a/tests/integrations/pymongo/test_pymongo.py b/tests/integrations/pymongo/test_pymongo.py index 0669f73c30..b57061b0a0 100644 --- a/tests/integrations/pymongo/test_pymongo.py +++ b/tests/integrations/pymongo/test_pymongo.py @@ -52,11 +52,13 @@ def test_transactions(sentry_init, capture_events, mongo_server, with_pii): common_tags = { "db.name": "test_db", "db.system": "mongodb", + "db.driver.name": "pymongo", "net.peer.name": mongo_server.host, "net.peer.port": str(mongo_server.port), } for span in find, insert_success, insert_fail: assert span["data"][SPANDATA.DB_SYSTEM] == "mongodb" + assert span["data"][SPANDATA.DB_DRIVER_NAME] == "pymongo" assert span["data"][SPANDATA.DB_NAME] == "test_db" assert span["data"][SPANDATA.SERVER_ADDRESS] == "localhost" assert span["data"][SPANDATA.SERVER_PORT] == mongo_server.port @@ -136,6 +138,7 @@ def test_breadcrumbs(sentry_init, capture_events, mongo_server, with_pii): assert crumb["data"] == { "db.name": "test_db", "db.system": "mongodb", + "db.driver.name": "pymongo", "db.operation": "find", "net.peer.name": mongo_server.host, "net.peer.port": str(mongo_server.port), diff --git a/tests/integrations/redis/test_redis.py b/tests/integrations/redis/test_redis.py index 1861e7116f..a251a82430 100644 --- a/tests/integrations/redis/test_redis.py +++ b/tests/integrations/redis/test_redis.py @@ -262,6 +262,7 @@ def test_db_connection_attributes_client(sentry_init, capture_events): assert span["op"] == "db.redis" assert span["description"] == "GET 'foobar'" assert span["data"][SPANDATA.DB_SYSTEM] == "redis" + assert span["data"][SPANDATA.DB_DRIVER_NAME] == "redis" assert span["data"][SPANDATA.DB_NAME] == "1" assert span["data"][SPANDATA.SERVER_ADDRESS] == "localhost" assert span["data"][SPANDATA.SERVER_PORT] == 63791 @@ -288,6 +289,7 @@ def test_db_connection_attributes_pipeline(sentry_init, capture_events): assert span["op"] == "db.redis" assert span["description"] == "redis.pipeline.execute" assert span["data"][SPANDATA.DB_SYSTEM] == "redis" + assert span["data"][SPANDATA.DB_DRIVER_NAME] == "redis" assert span["data"][SPANDATA.DB_NAME] == "1" assert span["data"][SPANDATA.SERVER_ADDRESS] == "localhost" assert span["data"][SPANDATA.SERVER_PORT] == 63791 diff --git a/tests/integrations/sqlalchemy/test_sqlalchemy.py b/tests/integrations/sqlalchemy/test_sqlalchemy.py index d2a31a55d5..843d736c39 100644 --- a/tests/integrations/sqlalchemy/test_sqlalchemy.py +++ b/tests/integrations/sqlalchemy/test_sqlalchemy.py @@ -127,6 +127,7 @@ class Address(Base): for span in event["spans"]: assert span["data"][SPANDATA.DB_SYSTEM] == "sqlite" + assert span["data"][SPANDATA.DB_DRIVER_NAME] == "pysqlite" assert span["data"][SPANDATA.DB_NAME] == ":memory:" assert SPANDATA.SERVER_ADDRESS not in span["data"] assert SPANDATA.SERVER_PORT not in span["data"] @@ -201,6 +202,7 @@ class Address(Base): for span in event["spans"]: assert span["data"][SPANDATA.DB_SYSTEM] == "sqlite" + assert span["data"][SPANDATA.DB_DRIVER_NAME] == "pysqlite" assert SPANDATA.DB_NAME not in span["data"] assert SPANDATA.SERVER_ADDRESS not in span["data"] assert SPANDATA.SERVER_PORT not in span["data"] From 78009c064d2da524a3d4ca73f2b47e27520d3b21 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Wed, 15 Apr 2026 11:13:00 -0400 Subject: [PATCH 2/4] Look at the Dialect for SQLAlchemy, and small cleanup --- sentry_sdk/integrations/asyncpg.py | 3 +-- sentry_sdk/integrations/sqlalchemy.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/asyncpg.py b/sentry_sdk/integrations/asyncpg.py index 09e283c5c3..29f3bad152 100644 --- a/sentry_sdk/integrations/asyncpg.py +++ b/sentry_sdk/integrations/asyncpg.py @@ -199,6 +199,7 @@ async def _inner(*args: "Any", **kwargs: "Any") -> "T": def _set_db_data(span: "Span", conn: "Any") -> None: span.set_data(SPANDATA.DB_SYSTEM, "postgresql") + span.set_data(SPANDATA.DB_DRIVER_NAME, "asyncpg") addr = conn._addr if addr: @@ -215,5 +216,3 @@ def _set_db_data(span: "Span", conn: "Any") -> None: user = conn._params.user if user: span.set_data(SPANDATA.DB_USER, user) - - span.set_data(SPANDATA.DB_DRIVER_NAME, "asyncpg") diff --git a/sentry_sdk/integrations/sqlalchemy.py b/sentry_sdk/integrations/sqlalchemy.py index 457cd4a428..a4354f4228 100644 --- a/sentry_sdk/integrations/sqlalchemy.py +++ b/sentry_sdk/integrations/sqlalchemy.py @@ -138,7 +138,7 @@ def _set_db_data(span: "Span", conn: "Any") -> None: span.set_data(SPANDATA.DB_SYSTEM, db_system) try: - driver = conn.engine.driver + driver = conn.dialect.driver if driver: span.set_data(SPANDATA.DB_DRIVER_NAME, driver) except Exception: From 7d42bc34c55434105cda87f4b51520702df22ed5 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Wed, 15 Apr 2026 11:21:30 -0400 Subject: [PATCH 3/4] Remove assertion that shouldn't be there because it won't be set in that code path --- tests/integrations/asyncpg/test_asyncpg.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integrations/asyncpg/test_asyncpg.py b/tests/integrations/asyncpg/test_asyncpg.py index a499fcc2ec..3ddce7ddda 100644 --- a/tests/integrations/asyncpg/test_asyncpg.py +++ b/tests/integrations/asyncpg/test_asyncpg.py @@ -554,7 +554,6 @@ async def test_query_source_enabled( assert SPANDATA.CODE_NAMESPACE in data assert SPANDATA.CODE_FILEPATH in data assert SPANDATA.CODE_FUNCTION in data - assert data.get(SPANDATA.DB_DRIVER_NAME) == "asyncpg" @pytest.mark.asyncio From 8714e0975d3d24c22c9e2ada645a5a8d31804ca3 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 16 Apr 2026 07:53:48 -0400 Subject: [PATCH 4/4] Update driver name --- sentry_sdk/integrations/redis/modules/queries.py | 2 +- tests/integrations/redis/test_redis.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/redis/modules/queries.py b/sentry_sdk/integrations/redis/modules/queries.py index 4712dc17c5..c7780099c3 100644 --- a/sentry_sdk/integrations/redis/modules/queries.py +++ b/sentry_sdk/integrations/redis/modules/queries.py @@ -44,7 +44,7 @@ def _get_db_span_description( def _set_db_data_on_span(span: "Span", connection_params: "dict[str, Any]") -> None: span.set_data(SPANDATA.DB_SYSTEM, "redis") - span.set_data(SPANDATA.DB_DRIVER_NAME, "redis") + span.set_data(SPANDATA.DB_DRIVER_NAME, "redis-py") db = connection_params.get("db") if db is not None: diff --git a/tests/integrations/redis/test_redis.py b/tests/integrations/redis/test_redis.py index a251a82430..84c5699d14 100644 --- a/tests/integrations/redis/test_redis.py +++ b/tests/integrations/redis/test_redis.py @@ -262,7 +262,7 @@ def test_db_connection_attributes_client(sentry_init, capture_events): assert span["op"] == "db.redis" assert span["description"] == "GET 'foobar'" assert span["data"][SPANDATA.DB_SYSTEM] == "redis" - assert span["data"][SPANDATA.DB_DRIVER_NAME] == "redis" + assert span["data"][SPANDATA.DB_DRIVER_NAME] == "redis-py" assert span["data"][SPANDATA.DB_NAME] == "1" assert span["data"][SPANDATA.SERVER_ADDRESS] == "localhost" assert span["data"][SPANDATA.SERVER_PORT] == 63791 @@ -289,7 +289,7 @@ def test_db_connection_attributes_pipeline(sentry_init, capture_events): assert span["op"] == "db.redis" assert span["description"] == "redis.pipeline.execute" assert span["data"][SPANDATA.DB_SYSTEM] == "redis" - assert span["data"][SPANDATA.DB_DRIVER_NAME] == "redis" + assert span["data"][SPANDATA.DB_DRIVER_NAME] == "redis-py" assert span["data"][SPANDATA.DB_NAME] == "1" assert span["data"][SPANDATA.SERVER_ADDRESS] == "localhost" assert span["data"][SPANDATA.SERVER_PORT] == 63791