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");