Logging

Logging Overview

evlog gives you three ways to log. Simple one-liners, wide events that accumulate context, and auto-managed request logging. Choose the right one for your use case.

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

Fire-and-forget structured logs. Replace console.log, consola, or pino with log.info, log.error, log.warn, log.debug.

Wide Events

Accumulate context over a unit of work (a script, job, queue task, or request) then emit a single comprehensive event.

Request Logging

Auto-managed wide events scoped to HTTP requests. Framework middleware creates the logger and emits it for you.

Quick Comparison

Simple Logging (log)

One event per call. No accumulation, no lifecycle management.

src/index.ts
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()

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:

server/api/checkout.post.ts
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

logcreateLogger / createRequestLoggerFramework middleware
Use caseQuick one-off eventsScripts, jobs, workers, queues, HTTP without a frameworkAPI routes with a framework integration
ContextSingle callAccumulate with set()Accumulate with set()
EmitImmediateManual emit()Automatic on response end
LifecycleNoneYou manage itFramework manages it
OutputConsole + drainConsole + drainConsole + drain + enrich
Start with log for quick structured logging. When you need to accumulate context across an operation, switch to createLogger (or createRequestLogger for HTTP contexts). When using a framework integration, the middleware handles everything, just call useLogger(event) to retrieve the logger.

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, and link fields
  • Sampling to control log volume in production
  • Zero dependencies, ~5 kB gzip

Next Steps