Tracing

db0 can emit tracing events for every query it runs.

#Overview

db0 uses tracing channels of the diagnostics channel module to emit traceable actions for query operations.

Common use cases for tracing are:

  • Query performance monitoring and profiling
  • Logging
  • Query debugging
  • Error tracking

#Enabling tracing

Tracing is opt-in. Wrap the database instance with withTracing from db0/tracing and use the returned instance:

import { createDatabase } from "db0";
import { withTracing } from "db0/tracing";
import sqlite from "db0/connectors/better-sqlite3";

const db = withTracing(createDatabase(sqlite({})));

// Queries made with `db` now emit tracing events
const rows = await db.sql`SELECT * FROM users`;

Only the wrapped instance emits events, so tracing costs nothing for databases you do not wrap. Wrapping an already wrapped instance is a no-op.

Tracing is looked up through process.getBuiltinModule("node:diagnostics_channel"), which requires Node.js 20.16+ or 22.3+. On older versions, or on runtimes where diagnostics channels are unavailable, withTracing returns the database unchanged and silently emits no events.

The wrapper delegates every call to the original instance, so connector, dialect, capabilities, disposed, getInstance() and dispose() behave exactly as they do on an untraced database. Two things differ:

  • db.exec() and the statement methods (all(), run(), get()) always return a promise. On a disposed database exec() rejects instead of throwing synchronously, so that connectors failing synchronously emit the same events as connectors failing asynchronously. Every built-in connector already returns promises from these methods, so nothing changes in practice.
  • The returned object is a plain object that forwards to the original, not the original itself. If you implement Database as a class, instanceof checks and any members outside the Database interface do not carry over to the traced instance.

#Query Tracing Channel

db0 uses db0.query for the tracing channel name.

To set up tracing, you need to subscribe to the tracing channel. Since query operations are asynchronous, you need to subscribe to the asyncEnd event as it signals the completion of the operation.

db0/tracing exports the name as QUERY_CHANNEL so you do not have to hardcode it:

import { tracingChannel } from "node:diagnostics_channel";
import { QUERY_CHANNEL } from "db0/tracing";

const queryChannel = tracingChannel(QUERY_CHANNEL);

queryChannel.subscribe({
  start: (data) => {
    console.log("start", data.query);
  },
  asyncEnd: (data) => {
    console.log("end", data.query, data.result);
  },
});

The event payload contains several properties that can be used to track the query operation:

  • query: The query string as db0 sees it — for sql it is the template rebuilt with ? placeholders. It is not always what reaches the server: the PostgreSQL connector rewrites ? to $1, $2, ... on its way out, so traced queries will not match PostgreSQL server logs verbatim.
  • method: The method used to execute the query (exec, sql, prepare.all, prepare.run, prepare.get).
  • connector: The name of the connector (e.g. sqlite, postgresql).
  • dialect: The dialect of the database connection (sqlite, libsql, postgresql or mysql).
  • result: The result of the query (available on the asyncStart and asyncEnd events).
  • error: The error that occurred during the query operation (available on the error, asyncStart and asyncEnd events).

The events are emitted in the following order:

  • On success: startendasyncStartasyncEnd
  • On failure: startenderrorasyncStartasyncEnd

Queries always settle asynchronously, even with connectors that fail synchronously, so asyncEnd is emitted for every traced query.

Note

Bound parameters are never included in the traced context. Note that the query string itself can still contain literal values, either from raw db.exec() strings or from static interpolations (${} inside {}) in the sql template. Diagnostics channels are process-wide, so any subscriber can read them.

#Next steps

Read more in Connectors.
Read more in Integrations.

db0  tiny sql connector.