Database spans
Trace MongoDB queries automatically with wrap_collection, or record SQL and NoSQL queries as database spans in Python.
MongoDB - wrap_collection
Wrap a synchronous PyMongo collection once and every operation is traced automatically. Import from recount.integrations.pymongo:
from recount.integrations.pymongo import wrap_collection
users_col = wrap_collection(db["users"])Then use it exactly as you would a normal collection:
async def trace_fn():
# db span recorded automatically - filter, collection, duration, found/not
user = users_col.find_one({"accountId": account_id})
results = list(
users_col.find({"status": "active", "plan": "enterprise"})
)
await recount.start_trace("Process application", trace_fn)Each span captures the operation name, collection, filter/pipeline, and a result summary (found, count, insertedId, etc.). Document contents are never recorded - only metadata about the query result.
Naming spans
By default the span name is collection.operation (e.g. users.find_one). wrap_collection methods are synchronous (PyMongo is a sync driver), so recount.named() - which awaits its callback - needs an async wrapper around the call:
async def load_account():
return users_col.find_one({"accountId": account_id})
user = await recount.named("Load account", load_account)
# -> db span named 'Load account' instead of 'users.find_one'Cursor iteration
find() and aggregate() return a wrapped cursor - however you drain it, one span is recorded once iteration finishes, with count set to however many documents were read. Standard iteration, list(), .next(), and the with context manager are all supported:
# iterate
for user in users_col.find({"status": "active"}):
process(user)
# list()
results = list(users_col.find({"status": "active"}))
# .next()
cursor = users_col.find({"status": "active"})
while True:
try:
user = cursor.next()
process(user)
except StopIteration:
break
# context manager
with users_col.find({"status": "active"}) as cursor:
for user in cursor:
process(user)close() finalizes the span too, for cases where you stop draining early. rewind() resets the count so a second drain records its own span.
Because wrap_collection wraps a synchronous driver, this only supports PyMongo's Collection - if your app uses Motor (the async MongoDB driver), wrap query call sites manually with recount.add_span("db", ...) instead.
Redacting filter values
If your filters contain PII, pass redact:
customers_col = wrap_collection(
db["customers"],
redact=["name", "billing.accountName", "billing.accountNumber"],
)Redact paths are applied to the span's input (the filter object). See Redaction for syntax.
Supported operations
find_one, find (cursor - see Cursor iteration), insert_one, insert_many, update_one, update_many, delete_one, delete_many, replace_one, find_one_and_update, find_one_and_delete, aggregate (cursor - see Cursor iteration), count_documents
Install
pip install "recount-sdk[pymongo]"Manual spans
For SQL databases or when you need full control, use add_span with type_="db" directly.
SQL
recount.add_span("db", "Fetch users", {
"metadata": {"table": "users", "queryType": "sql"},
"input": {"query": "SELECT * FROM users WHERE active = $1", "params": [True]},
"output": {"count": 42},
"durationMs": 12,
})NoSQL
recount.add_span("db", "Fetch users", {
"metadata": {"collection": "users", "queryType": "nosql"},
"input": {
"filter": {"active": True},
"projection": {"name": 1, "email": 1},
"sort": {"createdAt": -1},
},
"output": {"count": 42},
"durationMs": 8,
})Metadata fields
| Field | Type | Description |
|---|---|---|
queryType | "sql" | "nosql" | Controls how the query is displayed in the dashboard |
table | str | Table name (SQL), shown in the span header |
collection | str | Collection name (NoSQL), shown in the span header |
Output convention
Pass summary stats (counts, affected rows, timings) rather than raw documents.
Param redaction
Pass redactParams with the exact parameter values to redact from input.params:
recount.add_span("db", "User lookup", {
"metadata": {"table": "users", "queryType": "sql"},
"input": {"query": "SELECT * FROM users WHERE id = $1", "params": [user_id]},
"output": {"rows": 1},
"redactParams": [user_id],
})