Adapters
Adapters let you send logs to external observability platforms. evlog provides built-in adapters for popular services, and you can create custom adapters for any destination.
How Adapters Work
Adapters hook into the evlog:drain event, which fires after each request completes. The drain runs in fire-and-forget mode, meaning it never blocks the HTTP response.
server/plugins/evlog-drain.ts
import { createAxiomDrain } from 'evlog/axiom'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})
Serverless Support: On Cloudflare Workers and Vercel Edge, evlog automatically uses
waitUntil() to ensure drains complete before the runtime terminates. No additional configuration needed.Available Adapters
Standalone Usage (Without Nitro)
In plain TypeScript or Bun scripts, use the drain option in initLogger instead of Nitro hooks. Every emitted event is drained automatically.
index.ts
import type { DrainContext } from 'evlog'
import { initLogger, log, createRequestLogger } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'
import { createDrainPipeline } from 'evlog/pipeline'
const pipeline = createDrainPipeline<DrainContext>()
const drain = pipeline(createAxiomDrain())
initLogger({
env: { service: 'my-script' },
drain,
})
log.info({ action: 'job_started' }) // drained automatically
const reqLog = createRequestLogger({ method: 'POST', path: '/process' })
reqLog.set({ processed: 42 })
reqLog.emit() // drained automatically
await drain.flush()
See the full bun-script example for a realistic batch processing script.
Multiple Destinations
Send logs to multiple services simultaneously:
server/plugins/evlog-drain.ts
import { createAxiomDrain } from 'evlog/axiom'
import { createOTLPDrain } from 'evlog/otlp'
export default defineNitroPlugin((nitroApp) => {
const axiom = createAxiomDrain()
const otlp = createOTLPDrain()
nitroApp.hooks.hook('evlog:drain', async (ctx) => {
await Promise.allSettled([axiom(ctx), otlp(ctx)])
})
})
Drain Context
Every adapter receives a DrainContext with:
| Field | Type | Description |
|---|---|---|
event | WideEvent | The complete log event with all accumulated context |
request | object | Request metadata (method, path, requestId) |
headers | object | Safe HTTP headers (sensitive headers are filtered) |
Security: Sensitive headers (
authorization, cookie, x-api-key, etc.) are automatically filtered and never passed to adapters.Zero-Config Setup
All adapters support automatic configuration via environment variables. No code changes needed when deploying to different environments:
.env
# Axiom
NUXT_AXIOM_TOKEN=xaat-xxx
NUXT_AXIOM_DATASET=my-logs
# OTLP
NUXT_OTLP_ENDPOINT=https://otlp.example.com
# PostHog
NUXT_POSTHOG_API_KEY=phc_xxx
# Sentry
NUXT_SENTRY_DSN=https://key@o0.ingest.sentry.io/123
server/plugins/evlog-drain.ts
import { createAxiomDrain } from 'evlog/axiom'
export default defineNitroPlugin((nitroApp) => {
// Automatically reads from env vars
nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})