Configuration
evlog has two configuration surfaces: global options set once at startup, and middleware options set per-framework integration. This page documents both.
Global Options (initLogger)
These options apply to all frameworks. Call initLogger() once at application startup for standalone frameworks (Hono, Express, Fastify, Elysia, NestJS, SvelteKit, Cloudflare Workers). For Nuxt and Nitro, these are set via module config and passed through automatically.
import { initLogger } from 'evlog'
initLogger({
enabled: true,
env: { service: 'my-api', environment: 'production' },
pretty: false,
silent: false,
stringify: true,
sampling: { rates: { info: 10 }, keep: [{ status: 400 }] },
drain: createAxiomDrain(),
})
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable/disable all logging globally. When false, all operations become no-ops |
env | Partial<EnvironmentContext> | Auto-detected | Environment context overrides (see below) |
pretty | boolean | true in dev | Pretty print with tree formatting. Auto-detected based on NODE_ENV |
silent | boolean | false | Suppress console output. Events are still built, sampled, and passed to drains |
stringify | boolean | true | Emit JSON strings when pretty is disabled. Set to false for Cloudflare Workers |
sampling | SamplingConfig | undefined | Head and tail sampling configuration. See Sampling |
drain | (ctx: DrainContext) => void | undefined | Drain callback for sending events to external services |
Environment Context
The env option controls the fields included in every log event. Most values are auto-detected from environment variables and package.json.
| Field | Type | Default | Auto-detected from |
|---|---|---|---|
service | string | 'app' | SERVICE_NAME, package.json name |
environment | string | 'development' | NODE_ENV |
version | string | undefined | APP_VERSION, package.json version |
commitHash | string | undefined | COMMIT_SHA, GIT_COMMIT, VERCEL_GIT_COMMIT_SHA |
region | string | undefined | FLY_REGION, AWS_REGION, VERCEL_REGION |
Silent Mode
Use silent when your deployment platform captures stdout as its primary log ingestion (GCP Cloud Run, AWS Lambda, Fly.io, Railway, etc.) and you want a drain adapter to control the output format.
initLogger({
silent: process.env.NODE_ENV === 'production',
drain: createCloudLoggingDrain(),
})
silent is enabled without a drain, events are built and sampled but never output anywhere. evlog will warn you about this at startup.Middleware Options
These options are passed to the framework middleware/plugin. They control per-request behavior: which routes to log, how to drain and enrich events, and custom tail sampling logic.
app.use(evlog({
include: ['/api/**'],
exclude: ['/api/health'],
routes: { '/api/auth/**': { service: 'auth' } },
drain: createAxiomDrain(),
enrich: (ctx) => { ctx.event.region = process.env.FLY_REGION },
keep: (ctx) => { if (ctx.duration > 2000) ctx.shouldKeep = true },
}))
app.use(evlog({
include: ['/api/**'],
drain: createAxiomDrain(),
enrich: (ctx) => { ctx.event.region = process.env.FLY_REGION },
}))
await app.register(evlog, {
include: ['/api/**'],
drain: createAxiomDrain(),
})
| Option | Type | Default | Description |
|---|---|---|---|
include | string[] | undefined | Route glob patterns to log. If not set, all routes are logged |
exclude | string[] | undefined | Route patterns to exclude. Exclusions take precedence over inclusions |
routes | Record<string, { service: string }> | undefined | Route-specific service name overrides |
drain | (ctx: DrainContext) => void | undefined | Drain callback called with every emitted event |
enrich | (ctx: EnrichContext) => void | undefined | Enrich callback called after emit, before drain |
keep | (ctx: TailSamplingContext) => void | undefined | Custom tail sampling callback |
evlog:drain, evlog:enrich, evlog:emit:keep) instead of middleware options. See the Nuxt and Nitro pages.Middleware drain vs global drain
When a middleware drain is set, it takes precedence over the global drain from initLogger(). If no middleware drain is set, the global drain is used as fallback, with the benefit of receiving the full enriched event with request context (method, path, headers).
import { initLogger } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'
initLogger({
env: { service: 'my-api' },
drain: createAxiomDrain(), // fallback: used by singleton log API AND middleware (if no middleware drain)
})
app.use(evlog({
// no drain here - falls back to globalDrain from initLogger, with full request context
}))
Framework-Specific Options
Some frameworks have additional options beyond the shared config:
Nuxt
The Nuxt module accepts all global options and middleware options in nuxt.config.ts under the evlog key, plus:
| Option | Type | Default | Description |
|---|---|---|---|
console | boolean | true | Enable/disable browser console output (client-side only) |
transport.enabled | boolean | false | Send client logs to the server via API endpoint |
transport.endpoint | string | '/api/_evlog/ingest' | Custom transport endpoint |
See the full Nuxt configuration.
Nitro
The Nitro module accepts enabled, env, pretty, silent, sampling, include, exclude, and routes in nitro.config.ts. Drain and enrichment are done via Nitro hooks.
Client Logging
Capture browser events with structured logging. Same API as the server, with automatic console styling, user identity context, and optional server transport.
Performance
evlog adds ~7µs per request. Faster than pino, consola, and winston in most scenarios while emitting richer, more useful events.