Tracing
Set up OpenTelemetry tracing for distributed observability
Prerequisites
Install the OpenTelemetry API package:
npm install @opentelemetry/apiThe @opentelemetry/api package is an optional peer dependency. VibORM gracefully degrades to no-op when it's not installed.
Configuration
import { VibORM } from "viborm";
// Simple: enable with defaults
const client = VibORM.create({
schema,
driver,
instrumentation: {
tracing: true
}
});
// Custom: configure options
const clientCustom = VibORM.create({
schema,
driver,
instrumentation: {
tracing: {
includeSql: true, // Include SQL in spans (default: true)
includeParams: false // Exclude params (default: false)
}
}
});Options
| Option | Type | Default | Description |
|---|---|---|---|
includeSql | boolean | true | 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 (query execution wrapper)
│ └── viborm.driver.execute (actual database call)
└── 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 | Query execution wrapper |
viborm.driver.execute | Actual 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.) |
Driver execute span (viborm.driver.execute) adds SQL details when configured:
| Attribute | Condition | Description |
|---|---|---|
db.query.text | includeSql: true | SQL query text |
db.query.parameter.<index> | includeParams: true | Individual query parameters |
Example Span Attributes
// viborm.operation span
{
"db.system.name": "postgresql",
"db.system.driver": "pg",
"db.collection.name": "users",
"db.operation.name": "findMany"
}
// viborm.driver.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
const provider = new NodeTracerProvider();
// Export traces to your backend (Jaeger, Honeycomb, etc.)
provider.addSpanProcessor(
new SimpleSpanProcessor(new OTLPTraceExporter())
);
provider.register();
// Now create your VibORM client
const client = VibORM.create({
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 mutable state
- Each client instance has isolated tracer state
- Safe for Cloudflare Workers, Vercel Edge, and similar runtimes
SQL and Parameters
By default, SQL is included in spans but parameters are excluded:
// Default behavior
tracing: {
includeSql: true, // SQL visible in spans
includeParams: false // Params hidden
}Only enable includeParams in development. Query parameters in traces may contain sensitive data and could be stored in your tracing backend.
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();
provider.addSpanProcessor(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");