Skip to content

Open per-query tracing span in QueryLogger#4270

Open
Diggsey wants to merge 1 commit into
launchbadge:mainfrom
platformed-com:db-tracing-spans
Open

Open per-query tracing span in QueryLogger#4270
Diggsey wants to merge 1 commit into
launchbadge:mainfrom
platformed-com:db-tracing-spans

Conversation

@Diggsey
Copy link
Copy Markdown
Contributor

@Diggsey Diggsey commented May 21, 2026

QueryLogger now opens a hardcoded INFO-level db.query span in new() that closes on drop, with field names following the OTel database span semantic conventions (db.system.name, db.query.text, db.response.returned_rows, db.response.affected_rows, otel.kind = "client"). The existing close-time event is emitted inside the span via Span::in_scope so callers get both — the span for OTel correlation, the event for the existing rows_affected/elapsed_secs fields under whatever level LogSettings configures.

QueryLogger::new keeps its old signature; drivers attach the system name via the additive with_db_system_name builder, which records the db.system.name attribute on the already-open span.

QueryLogger stores Span, not EnteredSpan, so it stays Send across the postgres/mysql try_stream! paths that broke #3176. A small InstrumentedStream wrapper (using pin-project-lite, already present transitively via the async runtimes) enters the span on each poll_next and drops the guard before returning, so no guard is held across an await. SQLite's synchronous Iterator path just relies on the close-event in-scope emission.

Design constraints come from abonander's 2026-04-14 review on #3313: current OTel semconv field names, no tracing::enabled!() (broken per tokio-rs/tracing#2448), hardcoded verbosity rather than runtime- configurable span levels.

Does your PR solve an issue?

It's related to a number of issues but doesn't directly solve one.

Is this a breaking change?

No

This PR was written with assistance from Claude Code

QueryLogger now opens a hardcoded INFO-level `db.query` span in `new()`
that closes on drop, with field names following the OTel database span
semantic conventions (`db.system.name`, `db.query.text`,
`db.response.returned_rows`, `db.response.affected_rows`,
`otel.kind = "client"`). The existing close-time event is emitted inside
the span via `Span::in_scope` so callers get both — the span for OTel
correlation, the event for the existing `rows_affected`/`elapsed_secs`
fields under whatever level `LogSettings` configures.

`QueryLogger::new` keeps its old signature; drivers attach the system
name via the additive `with_db_system_name` builder, which records the
`db.system.name` attribute on the already-open span.

QueryLogger stores `Span`, not `EnteredSpan`, so it stays `Send` across
the postgres/mysql `try_stream!` paths that broke launchbadge#3176. A small
`InstrumentedStream` wrapper (using `pin-project-lite`, already present
transitively via the async runtimes) enters the span on each `poll_next`
and drops the guard before returning, so no guard is held across an
await. SQLite's synchronous `Iterator` path just relies on the
close-event in-scope emission.

Design constraints come from abonander's 2026-04-14 review on launchbadge#3313:
current OTel semconv field names, no `tracing::enabled!()` (broken per
tokio-rs/tracing#2448), hardcoded verbosity rather than runtime-
configurable span levels.
@Diggsey Diggsey force-pushed the db-tracing-spans branch from 519b53c to c8859ca Compare May 21, 2026 13:23
@Diggsey Diggsey marked this pull request as ready for review May 21, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant