Skip to content

feat: publish every query on the tracing channel reylo:query - #117

Merged
colinhacks merged 2 commits into
mainfrom
tracing-channel
Oct 24, 2026
Merged

colinhacks merged 2 commits into
mainfrom
tracing-channel

Conversation

@colinhacks

Copy link
Copy Markdown
Owner

Every query Reylo runs is now published on Node's tracing channel reylo:query, so a tracer or a logger can wrap each one in a span without wrapping the driver or every call.

import { tracingChannel } from "node:diagnostics_channel";
import type { QueryTrace } from "reylo";

tracingChannel<QueryTrace & { span: Span }>("reylo:query").subscribe({
  start(ctx) {
    ctx.span = tracer.startSpan("db.query", { attributes: { "db.query.summary": ctx.query.source, "db.query.text": ctx.query.sql } });
  },
  error(ctx) { ctx.span.recordException(ctx.error); },
  end(ctx) { if (ctx.sync) ctx.span.end(); },
  asyncEnd(ctx) { ctx.span.end(); },
});
  • One call per run. .run(), .try() and each call of the function .with() returns are one tracePromise; .runSync() and .trySync() one traceSync. Binding with .with() publishes nothing.
  • The context (QueryTrace, exported from reylo): the query (its .source and .sql name it), the client, the params, sync, and the result or error. A database error .try() returns as a value is published as an error too.
  • Spans nest. The call runs inside start.runStores, so a store a tracer binds to start is the current one while the driver runs, and the driver's own spans nest under the query's.
  • No cost and no import where nothing listens. src/trace.ts looks the module up through process.getBuiltinModule instead of importing it, so a bundle for a runtime without it still builds and runs its queries untraced. With no subscriber, no context is built. Bun's TracingChannel has no hasSubscribers of its own, so each event's channel is asked.
  • Runtimes checked: Node 26, Bun 1.4 (bun:sqlite), and workerd with nodejs_compat (and without it, where the channel is simply absent).

Docs: a Tracing subsection at the end of Usage, linked from .source; a sentence in the README; and a line in the using-reylo skill.

Tests: tests/trace.test.ts covers each method's events and their order, the context's fields, errors (the driver's and Reylo's row count), .try() and .trySync() publishing once, .with(), a store bound to start seen by the driver, and the subscriber check on each of the five events.

Copilot AI balanced review requested due to automatic review settings October 24, 2026 03:26
@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
reylo Ready Ready Preview Oct 24, 2026 4:33am UTC

Request Review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

I reviewed the reylo:query tracing channel end to end: the channel lookup, how it wraps each run method, the exported context type, the docs and the tests. tests/trace.test.ts passes locally on Node 24 (8 of 8).

  • src/trace.ts: finds node:diagnostics_channel at run time through process.getBuiltinModule, which every supported Node has (engines: >=24). listening() asks each of the five event channels for subscribers, so a query nobody traces builds no context.
  • src/index.ts: the old run is now runOnce. The new run wraps it in tracePromise, which covers .run(), .try() and the function .with() returns. .runSync() wraps its body in traceSync, and .trySync() goes through query.runSync, so each call publishes once. runOnce turns every throw into a rejection, so an async call always reaches asyncEnd, and the docs' end/asyncEnd split never leaves a span open.
  • QueryTrace: re-exported as a type from reylo. trace.ts imports only types from index.ts, so the two files don't import each other at run time.
  • Docs and skill: a ### Tracing subsection in 02-usage.mdx, plus a line in the README and in skills/using-reylo/SKILL.md. The twoslash block's import type { QueryTrace } from "reylo" resolves through site/lib/twoslash.ts, which mounts the repository's src/index.ts.
  • Tests: they check the event order for each method, publishing once for .try() and .trySync(), the driver running inside the store bound to start, and the subscriber check on all five events.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

Each .run(), .runSync(), .try(), .trySync() and call of the function
.with() returns is one call on node:diagnostics_channel's tracing
channel, carrying the query, client, params, sync, and its result or
error. The module is looked up at run time, so a runtime without it
runs queries untraced.
@colinhacks
colinhacks merged commit 1eecb3e into main Oct 24, 2026
7 checks passed
@colinhacks
colinhacks deleted the tracing-channel branch October 24, 2026 04:54
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.

2 participants