Wide Events
Wide events are the core concept behind evlog. Instead of scattering logs throughout your codebase, you accumulate context and emit a single, comprehensive log event.
Why Wide Events?
Traditional logging creates noise:
// Traditional approach - 6 separate log lines
logger.info('Request started')
logger.info('User authenticated', { userId: user.id })
logger.info('Fetching cart', { cartId: cart.id })
logger.info('Processing payment')
logger.info('Payment successful')
logger.info('Request completed', { duration: 234 })
This approach has problems:
- Scattered context: Information is spread across multiple log lines
- Hard to correlate: Matching logs to requests requires request IDs everywhere
- Noise: 10+ log lines per request makes finding issues harder
- Incomplete: Some logs might be missing if errors occur
Wide events solve this:
// server/api/checkout.post.ts
const log = useLogger(event)
log.set({ user: { id: 1, plan: 'pro' } })
log.set({ cart: { id: 42, items: 3, total: 9999 } })
log.set({ payment: { method: 'card', status: 'success' } })
// One log, all context - emitted automatically
[INFO] POST /api/checkout (234ms)
user: { id: 1, plan: 'pro' }
cart: { id: 42, items: 3, total: 9999 }
payment: { method: 'card', status: 'success' }
status: 200
Anatomy of a Wide Event
A well-designed wide event contains context from multiple layers:
Request Context
Basic information about the request itself:
log.set({
method: 'POST',
path: '/api/checkout',
requestId: 'abc-123-def',
traceId: 'trace-xyz-789',
})
User Context
Who is making the request:
log.set({
userId: user.id,
email: user.email,
subscription: user.plan,
accountAge: daysSince(user.createdAt),
})
Business Context
Domain-specific data relevant to the operation:
log.set({
cart: {
id: cart.id,
items: cart.items.length,
total: cart.total,
currency: 'USD',
},
shipping: {
method: 'express',
country: address.country,
},
coupon: appliedCoupon?.code,
})
Outcome
The result of the operation:
log.set({
status: 200,
duration: Date.now() - startTime,
success: true,
})
log.set({
status: 500,
error: {
message: err.message,
code: err.code,
type: err.constructor.name,
},
})
Best Practices
Use Meaningful Keys
// Avoid generic keys
log.set({ data: { id: 123 } })
// Use specific, descriptive keys
log.set({ order: { id: 123, status: 'pending' } })
Group Related Data
// Flat structure is hard to read
log.set({
userId: 1,
userEmail: 'a@b.com',
cartId: 2,
cartTotal: 100,
})
// Grouped structure is clearer
log.set({
user: { id: 1, email: 'a@b.com' },
cart: { id: 2, total: 100 },
})
Add Context Incrementally
Call log.set() as you gather information:
// server/api/checkout.post.ts
export default defineEventHandler(async (event) => {
const log = useLogger(event)
const user = await getUser(event)
log.set({ user: { id: user.id, plan: user.plan } })
const cart = await getCart(user.id)
log.set({ cart: { items: cart.items.length, total: cart.total } })
const payment = await processPayment(cart)
log.set({ payment: { method: payment.method, status: payment.status } })
return { success: true }
})
[INFO] POST /api/checkout (456ms)
user: { id: 1, plan: 'pro' }
cart: { items: 3, total: 9999 }
payment: { method: 'card', status: 'success' }
status: 200
Handle Errors Gracefully
When errors occur, the wide event still emits with error context:
// server/api/checkout.post.ts
export default defineEventHandler(async (event) => {
const log = useLogger(event)
try {
const result = await processPayment(cart)
return result
} catch (err) {
log.set({
error: {
message: err.message,
code: err.code,
type: err.constructor.name,
},
})
throw err
}
})
[ERROR] POST /api/checkout (123ms)
user: { id: 1, plan: 'pro' }
cart: { items: 3, total: 9999 }
error: {
message: 'Card declined',
code: 'CARD_DECLINED',
type: 'PaymentError'
}
status: 500
Output Formats
evlog automatically switches between formats based on environment:
[INFO] POST /api/checkout (234ms)
user: { id: 1, plan: 'pro' }
cart: { items: 3, total: 9999 }
payment: { method: 'card', status: 'success' }
{
"level": "info",
"method": "POST",
"path": "/api/checkout",
"duration": 234,
"user": { "id": 1, "plan": "pro" },
"cart": { "items": 3, "total": 9999 },
"payment": { "method": "card", "status": "success" }
}
Next Steps
- Typed Fields - Add compile-time type safety to your wide events
- Structured Errors - Learn how to create errors with actionable context
Agent Skills
AI-assisted code review and evlog adoption using Agent Skills. Let AI review your logging patterns and guide migration to wide events.
Structured Errors
Create errors that explain why they occurred and how to fix them. Add actionable context with why, fix, and link fields for humans and AI agents.