Skip to content

Commit c04b944

Browse files
authored
fix(asyncpg): Add db.query.text to streamed query spans (#6633)
Fixes PY-2546 Fixes #6632
1 parent de01d8b commit c04b944

3 files changed

Lines changed: 132 additions & 45 deletions

File tree

sentry_sdk/consts.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,12 @@ class SPANDATA:
518518
Example: postgresql
519519
"""
520520

521+
DB_QUERY_TEXT = "db.query.text"
522+
"""
523+
The database query being executed.
524+
Example: "SELECT * FROM users WHERE id = $1"
525+
"""
526+
521527
DB_SYSTEM_NAME = "db.system.name"
522528
"""
523529
An identifier for the database management system (DBMS) product being used. See OpenTelemetry's list of well-known DBMS identifiers.

sentry_sdk/tracing_utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,18 @@ def record_sql_queries(
164164
sentry_sdk.add_breadcrumb(message=query, category="query", data=data)
165165

166166
if has_span_streaming_enabled(client.options):
167+
additional_attributes = {}
168+
if query is not None:
169+
additional_attributes["db.query.text"] = query
170+
167171
with sentry_sdk.traces.start_span(
168172
name="<unknown SQL query>" if query is None else query,
169173
attributes={
170174
"sentry.origin": span_origin,
171175
"sentry.op": span_op_override_value
172176
if span_op_override_value
173177
else OP.DB,
178+
**additional_attributes,
174179
},
175180
) as span:
176181
yield span

tests/integrations/asyncpg/test_asyncpg.py

Lines changed: 121 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,11 @@ async def test_query_source_prepare(
13421342
assert connect_span["name"] == "connect"
13431343
assert query_span["name"] == "SELECT * FROM users WHERE name = $1"
13441344
assert segment["name"] == "test_segment"
1345+
1346+
assert (
1347+
query_span["attributes"][SPANDATA.DB_QUERY_TEXT]
1348+
== "SELECT * FROM users WHERE name = $1"
1349+
)
13451350
else:
13461351
events = capture_events()
13471352
with start_transaction(name="test_transaction", sampled=True):
@@ -1412,6 +1417,7 @@ async def test_cursor_iteration_creates_db_cursor_iter_spans(
14121417
assert len(cursor_iter_spans) == 5
14131418
for span in cursor_iter_spans:
14141419
assert span["attributes"]["sentry.op"] == OP.DB_CURSOR_ITERATOR
1420+
assert span["attributes"][SPANDATA.DB_QUERY_TEXT] == "SELECT * FROM users"
14151421
else:
14161422
events = capture_events()
14171423

@@ -1441,65 +1447,135 @@ async def test_cursor_iteration_creates_db_cursor_iter_spans(
14411447

14421448

14431449
@pytest.mark.asyncio
1444-
async def test_cursor_fetch_methods_create_spans(sentry_init, capture_events) -> None:
1450+
@pytest.mark.parametrize("span_streaming", [True, False])
1451+
async def test_cursor_fetch_methods_create_spans(
1452+
sentry_init, capture_events, capture_items, span_streaming
1453+
) -> None:
14451454
sentry_init(
14461455
integrations=[AsyncPGIntegration()],
14471456
traces_sample_rate=1.0,
14481457
enable_db_query_source=True,
14491458
db_query_source_threshold_ms=0,
1459+
_experiments={"trace_lifecycle": "stream" if span_streaming else "static"},
14501460
)
1451-
events = capture_events()
14521461

1453-
with start_transaction(name="test_transaction"):
1454-
conn: Connection = await connect(PG_CONNECTION_URI)
1462+
if span_streaming:
1463+
items = capture_items()
14551464

1456-
await conn.executemany(
1457-
"INSERT INTO users(name, password, dob) VALUES($1, $2, $3)",
1458-
[
1459-
("Bob", "secret_pw", datetime.date(1984, 3, 1)),
1460-
("Alice", "pw", datetime.date(1990, 12, 25)),
1461-
],
1465+
with sentry_sdk.traces.start_span(name="test_segment"):
1466+
conn: Connection = await connect(PG_CONNECTION_URI)
1467+
1468+
await conn.executemany(
1469+
"INSERT INTO users(name, password, dob) VALUES($1, $2, $3)",
1470+
[
1471+
("Bob", "secret_pw", datetime.date(1984, 3, 1)),
1472+
("Alice", "pw", datetime.date(1990, 12, 25)),
1473+
],
1474+
)
1475+
1476+
async with conn.transaction():
1477+
cur = await conn.cursor(
1478+
"SELECT * FROM users WHERE dob > $1", datetime.date(1970, 1, 1)
1479+
)
1480+
# These exercise the `_exec` patch
1481+
await cur.fetchrow()
1482+
await cur.fetchrow()
1483+
1484+
await conn.close()
1485+
1486+
sentry_sdk.flush()
1487+
1488+
spans = [item.payload for item in items]
1489+
1490+
assert len(spans) == 7
1491+
1492+
connect_span = spans[0]
1493+
executemany_span = spans[1]
1494+
begin_span = spans[2]
1495+
fetchrow_span_1 = spans[3]
1496+
fetchrow_span_2 = spans[4]
1497+
commit_span = spans[5]
1498+
_segment_span = spans[6]
1499+
1500+
assert connect_span["name"] == "connect"
1501+
assert (
1502+
executemany_span["name"]
1503+
== "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)"
1504+
)
1505+
assert begin_span["name"] == "BEGIN;"
1506+
assert fetchrow_span_1["name"] == "SELECT * FROM users WHERE dob > $1"
1507+
assert fetchrow_span_2["name"] == "SELECT * FROM users WHERE dob > $1"
1508+
assert commit_span["name"] == "COMMIT;"
1509+
1510+
assert (
1511+
fetchrow_span_1["attributes"][SPANDATA.DB_QUERY_TEXT]
1512+
== "SELECT * FROM users WHERE dob > $1"
1513+
)
1514+
assert (
1515+
fetchrow_span_2["attributes"][SPANDATA.DB_QUERY_TEXT]
1516+
== "SELECT * FROM users WHERE dob > $1"
14621517
)
14631518

1464-
async with conn.transaction():
1465-
cur = await conn.cursor(
1466-
"SELECT * FROM users WHERE dob > $1", datetime.date(1970, 1, 1)
1519+
for span in (fetchrow_span_1, fetchrow_span_2):
1520+
assert span["attributes"][SPANDATA.DB_SYSTEM_NAME] == "postgresql"
1521+
assert span["attributes"][SPANDATA.DB_DRIVER_NAME] == "asyncpg"
1522+
assert span["attributes"]["sentry.op"] == OP.DB_CURSOR_FETCH
1523+
assert span["attributes"]["sentry.origin"] == "auto.db.asyncpg"
1524+
1525+
else:
1526+
events = capture_events()
1527+
1528+
with start_transaction(name="test_transaction"):
1529+
conn: Connection = await connect(PG_CONNECTION_URI)
1530+
1531+
await conn.executemany(
1532+
"INSERT INTO users(name, password, dob) VALUES($1, $2, $3)",
1533+
[
1534+
("Bob", "secret_pw", datetime.date(1984, 3, 1)),
1535+
("Alice", "pw", datetime.date(1990, 12, 25)),
1536+
],
14671537
)
1468-
# These exercise the `_exec` patch
1469-
await cur.fetchrow()
1470-
await cur.fetchrow()
14711538

1472-
await conn.close()
1539+
async with conn.transaction():
1540+
cur = await conn.cursor(
1541+
"SELECT * FROM users WHERE dob > $1", datetime.date(1970, 1, 1)
1542+
)
1543+
# These exercise the `_exec` patch
1544+
await cur.fetchrow()
1545+
await cur.fetchrow()
14731546

1474-
(event,) = events
1547+
await conn.close()
1548+
1549+
(event,) = events
14751550

1476-
assert len(event["spans"]) == 6
1551+
assert len(event["spans"]) == 6
14771552

1478-
connect_span = event["spans"][0]
1479-
executemany_span = event["spans"][1]
1480-
begin_span = event["spans"][2]
1481-
fetchrow_span_1 = event["spans"][3]
1482-
fetchrow_span_2 = event["spans"][4]
1483-
commit_span = event["spans"][5]
1553+
connect_span = event["spans"][0]
1554+
executemany_span = event["spans"][1]
1555+
begin_span = event["spans"][2]
1556+
fetchrow_span_1 = event["spans"][3]
1557+
fetchrow_span_2 = event["spans"][4]
1558+
commit_span = event["spans"][5]
14841559

1485-
assert connect_span["description"] == "connect"
1486-
assert (
1487-
executemany_span["description"]
1488-
== "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)"
1489-
)
1490-
assert begin_span["description"] == "BEGIN;"
1491-
assert fetchrow_span_1["description"] == "SELECT * FROM users WHERE dob > $1"
1492-
assert fetchrow_span_2["description"] == "SELECT * FROM users WHERE dob > $1"
1493-
assert commit_span["description"] == "COMMIT;"
1494-
1495-
for span in (fetchrow_span_1, fetchrow_span_2):
1496-
assert span["data"]["db.cursor"] is not None
1497-
assert span["data"]["db.system"] == "postgresql"
1498-
assert span["data"]["db.driver.name"] == "asyncpg"
1499-
assert span["op"] == OP.DB_CURSOR_FETCH
1500-
assert span["origin"] == "auto.db.asyncpg"
1501-
_assert_query_source(
1502-
span,
1503-
False,
1504-
"test_cursor_fetch_methods_create_spans",
1560+
assert connect_span["description"] == "connect"
1561+
assert (
1562+
executemany_span["description"]
1563+
== "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)"
15051564
)
1565+
assert begin_span["description"] == "BEGIN;"
1566+
assert fetchrow_span_1["description"] == "SELECT * FROM users WHERE dob > $1"
1567+
assert fetchrow_span_2["description"] == "SELECT * FROM users WHERE dob > $1"
1568+
assert commit_span["description"] == "COMMIT;"
1569+
1570+
for span in (fetchrow_span_1, fetchrow_span_2):
1571+
assert span["data"]["db.cursor"] is not None
1572+
assert span["data"]["db.system"] == "postgresql"
1573+
assert span["data"]["db.driver.name"] == "asyncpg"
1574+
assert span["op"] == OP.DB_CURSOR_FETCH
1575+
assert span["origin"] == "auto.db.asyncpg"
1576+
1577+
_assert_query_source(
1578+
span,
1579+
span_streaming,
1580+
"test_cursor_fetch_methods_create_spans",
1581+
)

0 commit comments

Comments
 (0)