Configuration
evlog reads its options from evlog.config.ts at the root of your app. The same file drives your server and the CLI.
The config file
import { defineEvlog } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'
export default defineEvlog({
service: 'my-api',
sampling: { rates: { info: 10 }, keep: [{ status: 400 }] },
drain: createAxiomDrain(),
})
npx evlog init writes this file for you.
evlog looks for evlog.config.ts, .mts, .js or .mjs, starting in your app and walking up to the workspace root. The nearest file wins, so one file at the root of a monorepo covers every app that has none of its own.
Load it
Nuxt, Nitro and TanStack Start load the file for you. Other frameworks import it:
import { initLogger, toLoggerConfig, toMiddlewareOptions } from 'evlog'
import { evlog } from 'evlog/hono'
import config from '../evlog.config'
initLogger(toLoggerConfig(config))
app.use(evlog(toMiddlewareOptions(config)))
toLoggerConfig passes the logger options, and toMiddlewareOptions passes the request options. Each framework page shows the exact lines.
Precedence
The file applies first, and options you pass to the framework override it.
On Nuxt and Nitro, the override merges with the file key by key. The file's drain, enrich and keep are never overridden: they run alongside the evlog:* hooks, so an event reaches both. On other frameworks, a field you pass next to the file, as in evlog({ ...toMiddlewareOptions(config), exclude: ['/health'] }), replaces that field; it is not merged.
Options
| Option | Type | Default | Description |
|---|---|---|---|
service | string | SERVICE_NAME or 'app' | Service name on every event |
environment | string | NODE_ENV or 'development' | Environment name |
enabled | boolean | true | false turns every call into a no-op |
pretty | boolean | true in dev | Tree output instead of JSON |
silent | boolean | false | No console output; events still reach the drain |
stringify | boolean | true | Print JSON strings when pretty is off |
minLevel | LogLevel | 'debug' | Lowest level for the log API. Request events use sampling |
sampling | SamplingConfig | – | Head rates and tail keep rules. See Sampling |
redact | boolean | RedactConfig | true in production | See Redaction |
drain | (ctx) => void | – | Where events go. See Adapters |
plugins | EvlogPlugin[] | – | See Plugins |
include / exclude | string[] | – | Route globs to log or skip |
routes | Record<string, { service }> | – | Service name per route |
enrich | (ctx) => void | – | Add fields before the drain |
keep | (ctx) => void | – | Custom tail sampling |
dev | 'evlog' | 'nitro' | 'both' | object | 'evlog' | Dev terminal output. See Structured errors |
extends | EvlogConfig | – | Build on a shared preset. See Share settings |
map, logs | – | – | CLI only. See evlog config |
Nuxt also takes console and transport in nuxt.config.ts. See Nuxt.
Inline config for scripts
For a small standalone script, passing options straight to initLogger in the file that starts the logger is fine:
import { initLogger } from 'evlog'
initLogger({ env: { service: 'sync-job' } })