Quick Start
This guide covers the core APIs you'll use most often with evlog.
useLogger, log, createError, parseError). No import statements needed.Get evlog running in 2 minutes
log (Simple Logging)
The simplest way to use evlog. Fire-and-forget structured logs, anywhere in your code:
import { log } from 'evlog'
log.info('auth', 'User logged in')
log.error({ action: 'payment', error: 'card_declined' })
log.warn('cache', 'Cache miss')
10:23:45.612 [auth] User logged in
10:23:45.613 ERROR [my-app] action=payment error=card_declined
10:23:45.614 [cache] Cache miss
Two call styles:
- Tagged:
log.info('tag', 'message')for quick, readable console output - Structured:
log.info({ key: value })for rich events that flow through the drain pipeline
createLogger (Wide Events)
When you need to accumulate context across multiple steps of an operation, whether a script, background job, queue worker, or workflow, use createLogger:
import { initLogger, createLogger } from 'evlog'
initLogger({ env: { service: 'sync-worker' } })
const log = createLogger({ jobId: job.id, queue: 'emails' })
log.set({ batch: { size: 50 } })
log.set({ batch: { processed: 50 } })
log.emit()
10:23:45.612 INFO [sync-worker] in 1204ms
├─ jobId: job_abc123
├─ queue: emails
└─ batch: size=50 processed=50
createLogger() accepts any initial context as a plain object. It returns a logger with set, error, info, warn, emit, and getContext.
For HTTP request contexts specifically, use createRequestLogger() which pre-populates method, path, and requestId:
import { createRequestLogger } from 'evlog'
const log = createRequestLogger({ method: 'POST', path: '/api/checkout' })
createLogger and createRequestLogger, you must call log.emit() manually. In framework integrations, this happens automatically.useLogger (Retrieve the Request Logger)
When using a framework integration (Nuxt, Hono, Express, etc.), the middleware automatically creates a wide event logger on request start and emits it on response end. useLogger(event) retrieves that logger from the request context:
import { useLogger } from 'evlog'
export default defineEventHandler(async (event) => {
const log = useLogger(event)
log.set({ user: { id: 1, plan: 'pro' } })
log.set({ cart: { items: 3, total: 9999 } })
const order = await processCheckout()
log.set({ orderId: order.id })
return { success: true, orderId: order.id }
})
10:23:45.612 INFO [my-app] POST /api/checkout 200 in 234ms
├─ user: id=1 plan=pro
├─ cart: items=3 total=9999
└─ orderId: ord_abc123
useLogger doesn't create a logger, the framework middleware already did that. It just retrieves it from the event context so you can add data with set().When to use what
Use log | Use createLogger() / createRequestLogger() | Use useLogger(event) |
|---|---|---|
| Quick one-off events | Scripts, jobs, workers, queues, HTTP without a framework | API routes with a framework integration |
| No context accumulation needed | Accumulate context over an operation | Retrieve the request-scoped logger |
| Client-side logging | Wide events (one log per operation) | Access the auto-managed wide event |
Service Identification
In multi-service architectures, differentiate which service a log belongs to using either route-based configuration or explicit service names.
Route-Based Configuration
Configure service names per route pattern in your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['evlog/nuxt'],
evlog: {
env: {
service: 'default-service',
},
routes: {
'/api/auth/**': { service: 'auth-service' },
'/api/payment/**': { service: 'payment-service' },
'/api/booking/**': { service: 'booking-service' },
},
},
})
Logs from routes matching these patterns will automatically include the configured service name:
21:57:10.442 INFO [auth-service] POST /api/auth/login 200 in 1ms
├─ requestId: 88ced16a-bef2-4483-86cb-2b4fb677ea52
├─ user: id=user_123 email=demo@example.com
└─ action: login
Explicit Service Parameter
Override the service name for specific routes using the second parameter of useLogger:
import { useLogger } from 'evlog'
export default defineEventHandler((event) => {
const log = useLogger(event, 'legacy-service')
log.set({ action: 'process_legacy_request' })
return { success: true }
})
useLogger parameter > Route configuration > env.service > Auto-detected from environmentcreateError (Structured Errors)
Use createError() to throw errors with actionable context:
import { createError } from 'evlog'
throw createError({
message: 'Payment failed',
status: 402,
why: 'Card declined by issuer',
fix: 'Try a different payment method',
link: 'https://docs.example.com/payments/declined',
})
{
"statusCode": 402,
"message": "Payment failed",
"data": {
"why": "Card declined by issuer",
"fix": "Try a different payment method",
"link": "https://docs.example.com/payments/declined"
}
}
Error Fields
| Field | Required | Description |
|---|---|---|
message | Yes | What happened (user-facing) |
status | No | HTTP status code (default: 500) |
why | No | Technical reason (for debugging) |
fix | No | Actionable solution |
link | No | Documentation URL for more info |
cause | No | Original error (if wrapping) |
internal | No | Backend-only fields for logs and wide events — never included in HTTP JSON or parseError() |
Frontend Integration
Use parseError() to extract all error fields on the client:
import { parseError } from 'evlog'
export async function checkout(cart: Cart) {
try {
await $fetch('/api/checkout', { method: 'POST', body: cart })
} catch (err) {
const error = parseError(err)
toast.add({
title: error.message,
description: error.why,
color: 'error',
actions: error.link
? [{ label: 'Learn more', onClick: () => window.open(error.link) }]
: undefined,
})
if (error.fix) {
console.info(`Fix: ${error.fix}`)
}
}
}
log (Client-Side)
The same log API works on the client side, outputting to the browser console:
<script setup lang="ts">
async function handleCheckout() {
log.info('checkout', 'User initiated checkout')
try {
await $fetch('/api/checkout', { method: 'POST' })
log.info({ action: 'checkout', status: 'success' })
} catch (err) {
log.error({ action: 'checkout', error: 'failed' })
}
}
</script>
export function useAnalytics() {
function trackEvent(event: string, data?: Record<string, unknown>) {
log.info('analytics', `Event: ${event}`)
if (data) {
log.debug({ event, ...data })
}
}
return { trackEvent }
}
Next Steps
- Logging Overview: Understand all three logging modes
- Wide Events: Learn how to design effective wide events
- Typed Fields: Add compile-time type safety to your wide events
- Structured Errors: Master error handling with evlog
- Best Practices: Security guidelines and production tips