Frameworks
Cloudflare Workers
Using evlog with Cloudflare Workers — wide events, structured errors, and logging in Workers and Durable Objects.
The evlog/workers adapter provides factory functions for creating request-scoped loggers with Cloudflare-specific context. Unlike framework integrations, Workers require manual log.emit() calls since there is no middleware lifecycle to hook into.
Quick Start
1. Install
bun add evlog
2. Initialize and create request loggers
src/worker.ts
import { initWorkersLogger, createWorkersLogger } from 'evlog/workers'
initWorkersLogger({
env: { service: 'my-worker' },
})
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const log = createWorkersLogger(request)
log.set({ action: 'handle_request' })
// ... your handler logic
log.emit()
return Response.json({ ok: true })
},
}
createWorkersLogger(request) automatically extracts method, path, and cf-ray from the request.
You must call
log.emit() manually before returning a response. Workers don't have a request lifecycle hook to auto-emit.Wide Events
Build up context progressively, then emit at the end:
src/worker.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const log = createWorkersLogger(request)
const url = new URL(request.url)
log.set({ route: url.pathname })
const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(url.searchParams.get('userId')).first()
log.set({ user: { id: user.id, plan: user.plan } })
const orders = await env.DB.prepare('SELECT COUNT(*) as count FROM orders WHERE user_id = ?').bind(user.id).first()
log.set({ orders: { count: orders.count } })
log.emit()
return Response.json({ user, orders })
},
}
Terminal output
14:58:15 INFO [my-worker] GET /api/users 200 in 12ms
├─ orders: count=5
├─ user: id=usr_123 plan=pro
├─ route: /api/users
└─ requestId: 4a8ff3a8-...
Error Handling
Use createError for structured errors and handle them with try/catch:
src/worker.ts
import { createError, parseError } from 'evlog'
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const log = createWorkersLogger(request)
try {
const body = await request.json()
log.set({ payment: { amount: body.amount } })
if (body.amount <= 0) {
throw createError({
status: 400,
message: 'Invalid payment amount',
why: 'The amount must be a positive number',
fix: 'Pass a positive integer in cents',
})
}
log.emit()
return Response.json({ success: true })
} catch (error) {
log.error(error instanceof Error ? error : new Error(String(error)))
log.emit()
const parsed = parseError(error)
return Response.json({
message: parsed.message,
why: parsed.why,
fix: parsed.fix,
}, { status: parsed.status })
}
},
}
Drain & Enrichers
Configure drain and enrichers via initWorkersLogger options:
src/worker.ts
import { initWorkersLogger, createWorkersLogger } from 'evlog/workers'
import { createAxiomDrain } from 'evlog/axiom'
import { createUserAgentEnricher } from 'evlog/enrichers'
import { createDrainPipeline } from 'evlog/pipeline'
import type { DrainContext } from 'evlog'
const pipeline = createDrainPipeline<DrainContext>({
batch: { size: 50, intervalMs: 5000 },
})
const drain = pipeline(createAxiomDrain())
const userAgent = createUserAgentEnricher()
initWorkersLogger({
env: { service: 'my-worker' },
drain,
enrich: (ctx) => {
userAgent(ctx)
},
})
Wrangler Configuration
Disable Cloudflare's default invocation logs to avoid duplicates when using evlog:
wrangler.toml
[observability]
enabled = false
Run Locally
wrangler dev