Getting Started
Quick Start
Get up and running with evlog in minutes.
This guide covers the core APIs you'll use most often with evlog.
useLogger (Nuxt/Nitro)
In API routes, use useLogger(event) to get a request-scoped logger:
// server/api/checkout.post.ts
export default defineEventHandler(async (event) => {
const log = useLogger(event)
log.set({ user: { id: 1, plan: 'pro' } })
log.set({ cart: { items: 3, total: 9999 } })
// Auto-emits on request end
return { success: true }
})
10:23:45.612 INFO [my-app] POST /api/checkout 200 in 234ms
├─ user: id=1 plan=pro
└─ cart: items=3 total=9999
The logger automatically emits when the request ends. No manual
emit() call needed.createError (Structured Errors)
Use createError() to throw errors with actionable context:
// server/api/checkout.post.ts
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) |
Frontend Integration
Use parseError() to extract all error fields on the client:
composables/useCheckout.ts
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)
// Direct access to all fields
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 (Simple Logging)
For quick one-off logs anywhere in your code:
// server/utils/auth.ts
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
Prefer wide events (
useLogger) over simple logs when possible. Use log for truly one-off events that don't belong to a request.Wide Event Fields
Every wide event should include context from different layers:
// server/api/checkout.post.ts
const log = useLogger(event)
// Request context (often auto-populated)
log.set({ method: 'POST', path: '/api/checkout' })
// User context
log.set({ userId: 1, subscription: 'pro' })
// Business context
log.set({ cart: { items: 3, total: 9999 }, coupon: 'SAVE10' })
// Outcome
log.set({ status: 200, duration: 234 })
{
"level": "info",
"method": "POST",
"path": "/api/checkout",
"userId": 1,
"subscription": "pro",
"cart": { "items": 3, "total": 9999 },
"coupon": "SAVE10",
"status": 200,
"duration": 234
}
Next Steps
- Wide Events - Learn how to design effective wide events
- Structured Errors - Master error handling with evlog