Logging Overview
evlog provides three logging APIs, each designed for a different context. You can use all three in the same project.
The Three Modes
Simple Logging
console.log, consola, pino, or winston with log.info, log.error, log.warn, log.debug — same level filtering, drain pipeline, redaction, and pretty/JSON output.Quick Comparison
Simple Logging (log)
One event per call. No accumulation, no lifecycle management.
import { log } from 'evlog'
log.info('auth', 'User logged in')
log.error({ action: 'payment', error: 'card_declined', userId: 42 })
Wide Events (createLogger / createRequestLogger)
One event per unit of work. Accumulate context progressively, emit when done.
import { createLogger } from 'evlog'
const log = createLogger({ jobId: 'sync-001', queue: 'emails' })
log.set({ batch: { size: 50, processed: 50 } })
log.emit()
import { createRequestLogger } from 'evlog'
const log = createRequestLogger({ method: 'POST', path: '/api/checkout' })
log.set({ user: { id: 1, plan: 'pro' } })
log.emit()
createRequestLogger is a thin wrapper around createLogger that pre-populates method, path, and requestId.
Request Logging (framework middleware)
Framework integrations create a wide event logger automatically on each request. useLogger(event) retrieves the logger that's already attached to the request context:
import { useLogger } from 'evlog'
export default defineEventHandler(async (event) => {
const log = useLogger(event)
log.set({ user: { id: 1, plan: 'pro' } })
return { success: true }
// auto-emitted on response end
})
useLogger(event) doesn't create a logger, it retrieves the one the framework middleware already attached to the event. Each framework has its own way to access it (useLogger, req.log, c.get('log'), etc.). In Nuxt, useLogger is auto-imported.When to Use What
log | createLogger / createRequestLogger | Framework middleware | |
|---|---|---|---|
| Use case | Quick one-off events | Scripts, jobs, workers, queues, HTTP without a framework | API routes with a framework integration |
| Context | Single call | Accumulate with set() | Accumulate with set() |
| Emit | Immediate | Manual emit() | Automatic on response end |
| Lifecycle | None | You manage it | Framework manages it |
| Output | Console + drain | Console + drain | Console + drain + enrich |
By context
| Context | Best fit | Why |
|---|---|---|
| HTTP route in Nuxt / Next / Hono / Express / … | useLogger(event) via framework integration | One wide event per request, auto-emitted on response end |
| HTTP handler without a framework | createRequestLogger({ method, path }) | Same shape as framework middleware, manual emit |
| CLI tool / one-shot script | log.* for steps + createLogger for the run summary — see Standalone | Pretty in dev, structured in CI, one summary event for the whole run |
| Published library | createLogger only — never initLogger — see Standalone | Don't pollute the host app's global config or force a drain on consumers |
| Background job / queue worker / cron | createLogger({ jobId, queue }) per invocation — see Standalone | One wide event per job run, perfect for retry analysis |
| Cloudflare Worker / edge function | createWorkersLogger(req) or createRequestLogger — see Cloudflare Workers | Per-request event, no process globals required |
| AWS Lambda | initLogger once + createLogger per invocation — see AWS Lambda | Cold-start init, per-event scope, drain flush in the handler |
| Batch / pipeline step | createLogger({ step }) per stage | One event per stage with inputs and outputs side by side |
| AI agent / LLM call | createLogger + createAILogger | Token usage, tool calls, streaming metrics on the same wide event |
| Library function called inside a request | useLogger(event) from caller, or accept a logger as argument | Inherit the parent's request context, contribute to the same wide event |
| Shared workspace package | Treat it like a library — see Standalone | Host app owns initLogger / drain; packages use createLogger or accept a logger |
log and createLogger in the same file when it makes sense — they share the global drain, redaction, and types.Shared Features
All three modes share the same foundation:
- Pretty output in development, JSON in production (default, no configuration needed)
- Drain pipeline to send events to Axiom, Sentry, PostHog, and more
- Structured errors with
why,fix, andlink, plus optional backend-onlyinternalfor logs - Sampling to control log volume in production
- Zero dependencies, ~6 kB gzip
Next Steps
- Simple Logging: The
logAPI in detail - Wide Events: Accumulating context and emitting events
- Structured Errors: Errors with actionable context
- Frameworks: Auto-managed request logging per framework
vs Other Loggers
Side-by-side comparison of evlog with pino, winston, and consola. Feature parity matrix, honest gaps, and migration snippets so you can switch with no surprises.
Simple Logging
evlog's general-purpose logger. A drop-in for console.log, pino, or consola, with the same level filtering, drain pipeline, redaction, and pretty/JSON output as wide events.