Get Started

Logging that
makes sense.

Wide events and structured errors for TypeScript. One log per request. Full context. Errors that explain why.

Which request failed?Who was the user?

Everything you need.

Wide Events

Accumulate context throughout your request. Emit once at the end with everything you need.

log.set({ user: { id, plan } })
log.set({ cart: { items, total } })
// → One event with all context

Structured Errors

Errors that explain why they happened and how to fix them.

throw createError({
  message: 'Payment failed',
  why: 'Card declined',
  fix: 'Try another card',
})

Log Draining

Send logs to external services in fire-and-forget mode. Never blocks your response.

nitroApp.hooks.hook('evlog:drain',
  async (ctx) => {
    await sendToAxiom(ctx.event)
  }
)

Built-in Adapters

Zero-config adapters for Axiom, OTLP, PostHog, Sentry, Better Stack, or build your own.

import { createAxiomDrain } from 'evlog/axiom'
import { createOTLPDrain } from 'evlog/otlp'
import { createPostHogDrain } from 'evlog/posthog'
import { createSentryDrain } from 'evlog/sentry'
import { createBetterStackDrain } from 'evlog/better-stack'

Smart Sampling

Head and tail sampling. Keep errors and slow requests, reduce noise.

sampling: {
  rates: { info: 10, warn: 50 },
  keep: [{ status: 400 }]
}

Nuxt & Nitro

First-class integration. Auto-create loggers, auto-emit at request end.

export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
})

Next.js

App Router support with AsyncLocalStorage. Wrap handlers, get full observability.

import { createEvlog } from 'evlog/next'

export const { withEvlog, useLogger } = createEvlog({
  service: 'my-app',
})

Client Transport

Send browser logs to your server. Automatic enrichment with server context.

// Browser
log.info({ action: 'click' })
// → Sent to /api/_evlog/ingest
// → Enriched & drained server-side

Pretty & JSON

Human-readable in dev, machine-parseable JSON in production.

[INFO] POST /api/checkout (234ms)
  user: { id: 1, plan: "pro" }
  cart: { items: 3 }

Enrichers

Auto-enrich events with user agent, geo, request size, and W3C trace context.

import { createUserAgentEnricher } from 'evlog/enrichers'
import { createGeoEnricher } from 'evlog/enrichers'
import { createTraceContextEnricher } from 'evlog/enrichers'

Hono & Workers

Standalone API for Hono, Cloudflare Workers, Bun, or any TypeScript runtime.

import { initLogger, createRequestLogger } from 'evlog'

const log = createRequestLogger({ method, path })
log.set({ user: { id, plan } })
log.emit()

Agent-Ready

Structured JSON output that AI agents can parse and understand.

{
  "level": "error",
  "why": "Card declined",
  "fix": "Try another card"
}

Three lines of code.
Full observability.

checkout.post.ts
export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  log.set({ user: { id: user.id, plan: user.plan } })
  log.set({ cart: { items: 3, total: 9999 } })

  return { success: true }
})
outputINFO
INFOPOST/api/checkout(234ms)
user: { id: 1842, plan: "pro" }
cart: { items: 3, total: 9999 }
status: 200
requestId: "req_8f2k..."

One log with full context

Errors that explain why.

payment.post.ts
throw createError({
  message: 'Payment failed',
  status: 402,
  why: 'Card declined by issuer',
  fix: 'Try a different card',
})
outputERROR
ERRORPOST/api/payment402
message: "Payment failed"
why: "Card declined by issuer"
fix: "Try a different card"

Actionable error messages

Stop grep-ing through chaos.

Wide events, structured errors, zero config. Ship with the logging your future self will thank you for.

Copyright © 2026