Database Instrumentation
Automatic database instrumentation with OTLP span creation for observability backends (Jaeger, Honeycomb), plus query logging for Prisma and Drizzle ORMs with slow query detection, parameter sanitization, and precise timing metrics.
Prisma Setup
Basic Setup
Wrap your Prisma client with Vestig logging:
Manual Event Handler
For more control, use the event handler directly:
PostgreSQL Auto-Instrumentation (Recommended)
For OTLP span creation and precise timing metrics, use instrumentPostgres() to wrap your postgres-js client at the driver level:
What Gets Instrumented
instrumentPostgres() automatically instruments:
- Template literal queries:
sql\SELECT * FROM users`` - Unsafe queries:
sql.unsafe('SELECT * FROM users') - Transactions:
sql.begin(async (tx) => { ... }) - SQL files:
sql.file('path/to/query.sql')
OTLP Span Attributes
Each query creates a span with OpenTelemetry semantic conventions:
| Attribute | Description | Example |
|---|---|---|
db.system |
Database system | postgresql |
db.operation |
SQL operation | SELECT, INSERT, UPDATE, DELETE |
db.statement |
SQL query (sanitized) | SELECT * FROM users WHERE id = $1 |
db.sql.table |
Table name (if detectable) | users |
db.duration_ms |
Query duration | 42 |
db.rows_affected |
Rows returned/affected | 10 |
db.slow_query |
Exceeded threshold | true |
db.name |
Database name (if configured) | myapp_production |
Integration with registerVestig()
Configure database instrumentation globally in your instrumentation.ts:
Then instrumentPostgres() will automatically use these settings:
Why Use instrumentPostgres()?
| Feature | Drizzle Logger | instrumentPostgres() |
|---|---|---|
| OTLP Spans | ❌ | ✅ |
| Precise Timing | ⚠️ Approximate | ✅ Exact |
| Transaction Spans | ❌ | ✅ |
| Global Config | ❌ | ✅ |
| Observability Backends | ❌ | ✅ Jaeger, Honeycomb, etc. |
Drizzle Logger (Alternative)
If you only need logging (no OTLP spans), use the Drizzle logger:
With Query Timing
Use measureQuery for explicit timing:
Configuration
Both Prisma and Drizzle wrappers accept the same configuration:
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true (dev) / false (prod) |
Enable/disable logging |
slowQueryThreshold |
number |
100 |
Threshold in ms for slow query warnings |
logLevel |
'all' | 'slow' | 'none' |
'all' (dev) / 'slow' (prod) |
Which queries to log |
sanitizeParams |
boolean |
true |
Redact sensitive parameters |
maxQueryLength |
number |
1000 |
Truncate long queries |
namespace |
string |
'db' |
Logger namespace |
onQuery |
(entry: QueryLogEntry) => void |
undefined |
Custom callback for each query |
Query Log Entry
Each logged query produces an entry with this structure:
Slow Query Detection
Queries exceeding the threshold are flagged and logged at warn level:
Output:
Parameter Sanitization
Sensitive parameters are automatically redacted:
Production Configuration
For production, log only slow queries:
Custom Query Callbacks
Process queries for custom analytics:
Correlation with Requests
Queries are automatically correlated with the current request context:
Log output includes context:
Best Practices
1. Use Appropriate Thresholds
Set thresholds based on your application:
2. Enable Only Slow Logging in Production
Reduce log volume without missing issues:
3. Combine with Dev Overlay
View queries in the Dev Overlay during development:
4. Index Slow Queries
Use the onQuery callback to track and address slow queries:
Supported Databases
Works with any database supported by Prisma or Drizzle:
- PostgreSQL
- MySQL
- SQLite
- SQL Server
- MongoDB (Prisma only)
- PlanetScale
- Neon
- Supabase
Next Steps
- Dev Overlay — View queries in real-time
- Tracing — Correlate queries with requests
- Server Components — Database in RSC