Skip to content
VibORM
Esc
navigateopen⌘Jpreview
On this page

Tracing

Set up OpenTelemetry tracing for distributed observability

Prerequisites

Install the OpenTelemetry API package:

npm install @opentelemetry/api

Configuration

import { createClient } from "viborm";

// Simple: enable with defaults
const client = createClient({
  schema,
  driver,
  instrumentation: {
    tracing: true
  }
});

// Custom: configure options
const clientCustom = createClient({
  schema,
  driver,
  instrumentation: {
    tracing: {
      includeSql: true,     // Explicitly include SQL in spans
      includeParams: false  // Exclude params (default: false)
    }
  }
});

Options

Option Type Default Description
includeSql boolean false Include SQL query text in span attributes
includeParams boolean false Include query parameters in span attributes
ignoreSpanTypes Array<string | RegExp> [] Span names to skip

Span Hierarchy

VibORM creates a hierarchy of spans for each operation:

viborm.operation (root span for client method call)
├── viborm.validate  (input validation)
├── viborm.build     (SQL construction)
├── viborm.execute   (database query execution)
└── viborm.parse     (result parsing)

For transactions:

viborm.transaction (transaction wrapper)
├── viborm.operation
├── viborm.operation
└── ...

Span Names

Span Name Description
viborm.operation High-level client method call
viborm.validate Schema validation of arguments
viborm.build SQL query construction
viborm.execute Database round-trip
viborm.parse Result hydration
viborm.transaction Transaction boundaries
viborm.connect Connection establishment
viborm.disconnect Connection teardown

Span Attributes

VibORM follows OTel database semantic conventions where possible.

Attributes by Span Type

All spans include these base attributes:

Attribute Description
db.system.name Database system (postgresql, sqlite)
db.system.driver Driver name (postgres, pg, pglite, sqlite3)

Operation spans (viborm.operation, viborm.validate, viborm.build, viborm.execute, viborm.parse) add:

Attribute Description
db.collection.name Table name (e.g., users)
db.operation.name Operation (findMany, create, update, etc.)
viborm.correlation.id Stable identifier for the originating ORM operation

Execute spans (viborm.execute) add SQL details when configured:

Attribute Condition Description
db.query.text includeSql: true SQL query text
db.query.parameter.<index> includeParams: true Individual query parameters

Cache spans omit cache.key, including for custom cache keys — see Privacy & Security.

Example Span Attributes

// viborm.operation span
{
  "db.system.name": "postgresql",
  "db.system.driver": "pg",
  "db.collection.name": "users",
  "db.operation.name": "findMany",
  "viborm.correlation.id": "operation-correlation-id"
}

// viborm.execute span (with includeSql: true)
{
  "db.system.name": "postgresql",
  "db.system.driver": "pg",
  "db.collection.name": "users",
  "db.operation.name": "findMany",
  "db.query.text": "SELECT * FROM \"users\" WHERE \"active\" = $1"
}

// viborm.transaction span
{
  "db.system.name": "postgresql",
  "db.system.driver": "pg"
}

Full Setup Example

To export traces, you need to configure an OpenTelemetry SDK:

import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";

// Set up the trace provider, exporting to your backend (Jaeger, Honeycomb, etc.)
const provider = new NodeTracerProvider({
  spanProcessors: [new SimpleSpanProcessor(new OTLPTraceExporter())],
});

provider.register();

// Now create your VibORM client
const client = createClient({
  schema,
  driver,
  instrumentation: {
    tracing: true
  }
});

Filtering Spans

Use ignoreSpanTypes to skip specific spans:

tracing: {
  ignoreSpanTypes: [
    "viborm.validate",  // Skip validation spans
    /viborm\.parse/     // Regex patterns also work
  ]
}

Graceful Degradation

VibORM uses dynamic imports for OpenTelemetry. If the package is not installed:

  • No errors are thrown
  • Operations execute normally
  • Spans are simply not created

This allows you to enable tracing only in environments where it’s needed without affecting other deployments.

Serverless Compatibility

VibORM’s tracer is designed for serverless environments:

  • No module-level request context
  • Each client instance has isolated tracer state
  • Safe for Cloudflare Workers, Vercel Edge, and similar runtimes

Operation attribution is snapshotted before asynchronous provider work starts. Concurrent operations sharing a driver or cache therefore retain their own model, operation, correlation ID, tracer, and logger. Instrumentation failures are observational and cannot replace the application result or execute the application callback twice.

SQL and Parameters

By default, both SQL and parameters are excluded from spans:

// Default behavior
tracing: {
  includeSql: false,     // SQL hidden
  includeParams: false   // Params hidden
}

These options affect tracing only — logging and error diagnostics have independent disclosure policies.

Testing with In-Memory Exporter

For testing, use an in-memory exporter:

import { InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";

const exporter = new InMemorySpanExporter();
const provider = new NodeTracerProvider({
  spanProcessors: [new SimpleSpanProcessor(exporter)],
});
provider.register();

// Run your queries...
await client.user.findMany({});

// Check the spans
const spans = exporter.getFinishedSpans();
const spanNames = spans.map(s => s.name);

expect(spanNames).toContain("viborm.operation");
expect(spanNames).toContain("viborm.execute");

Was this page helpful?