# withDbSpan ## Purpose `withDbSpan` is a thin OTel tracing helper that wraps any async database operation in a named span. It handles the boilerplate of starting a span, setting its status on success or error, recording exceptions, and always ending the span — so concrete adapters and migration runners can add tracing with a single wrapper call rather than repeating the span lifecycle pattern in every method. Like the instruments module, `withDbSpan` relies on the OTel no-op tracer when no SDK is configured, so it is safe to call in any environment including tests with no OTel setup. ## Features - Creates a named span via `trace.getTracer("@suaveplan/db")` - Sets `SpanStatusCode.OK` on successful completion - Sets `SpanStatusCode.ERROR` and records the exception on throw - Always ends the span (via `finally`) — no span leaks - Accepts optional `Attributes` for span metadata (e.g. `db.name`, `db.operation`) - Works with the OTel no-op provider — zero overhead when no exporter is registered ## Usage: Basic Example ```ts import { withDbSpan } from "@suaveplan/db/telemetry/trace"; const rows = await withDbSpan("db.query", async () => { return driver.query("SELECT * FROM users"); }); ``` ## Usage: Advanced Example ```ts import { withDbSpan } from "@suaveplan/db/telemetry/trace"; // With span attributes for richer trace data const result = await withDbSpan( "db.execute", async () => driver.execute("DELETE FROM sessions WHERE expires_at < ?", [Date.now()]), { "db.name": "app", "db.operation": "execute", "db.statement": "DELETE FROM sessions", } ); // Wrapping a full transaction const transferResult = await withDbSpan("db.transaction.transfer", async () => { return adapter.transaction(async (tx) => { await tx.execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", [100, "sender"]); await tx.execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", [100, "receiver"]); return { ok: true }; }); }, { "transfer.amount": 100 }); ``` ## API Reference ### `withDbSpan` ```ts withDbSpan( name: string, fn: () => Promise, attrs?: Attributes ): Promise ``` - **`name`** — OTel span name (e.g. `"db.query"`, `"db.migrate"`) - **`fn`** — async function to execute inside the span - **`attrs`** — optional `Attributes` map attached to the span at creation time - **Returns** — the resolved value of `fn()` - **Throws** — re-throws whatever `fn()` throws after recording the exception on the span ## Implementation Notes `withDbSpan` uses the module-level `tracer` constant (created once at import time via `trace.getTracer("@suaveplan/db")`) rather than calling `getTracer` on every invocation. The OTel spec guarantees that `getTracer` with the same name returns the same tracer instance, so this is a safe optimisation. Non-Error thrown values (e.g. strings, numbers) are wrapped in `new Error(String(err))` before being passed to `span.recordException`. This satisfies the OTel SDK's type requirement for `recordException` while still re-throwing the original value, so callers that catch strings continue to work as before.