框架
Cloudflare Workers
宽事件、结构化错误和日志记录,适用于 Cloudflare Workers 和持久对象。
evlog/workers 适配器提供了用于创建具有 Cloudflare 特定上下文的请求范围日志记录器的工厂函数。与框架集成不同,Workers 需要手动调用 log.emit(),因为没有可以钩入的中间件生命周期。
在我的 Cloudflare Worker 中设置 evlog
快速开始
1. 安装
pnpm add evlog
bun add evlog
yarn add evlog
npm install evlog
2. 初始化并创建请求日志记录器
src/worker.ts
import { defineWorkerFetch, initWorkersLogger } from 'evlog/workers'
initWorkersLogger({
env: { service: 'my-worker' },
})
export default defineWorkerFetch(async (request, _env, _ctx, log) => {
log.set({ action: 'handle_request' })
// ... 你的处理程序逻辑
log.emit()
return Response.json({ ok: true })
})
defineWorkerFetch 会替你将 ExecutionContext 传入 createWorkersLogger,因此异步的 drain 调用(PostHog、Axiom、……)会通过 waitUntil 在响应返回后继续存活。仅当你不想使用包装器时,才使用原始的 export default { fetch } + createWorkersLogger(request, { executionCtx: ctx })。
createWorkersLogger 仍会自动从请求中提取 method、path 和 cf-ray。
你必须在返回响应之前手动调用
log.emit()。Workers 没有可自动发射日志的请求生命周期钩子。使用 defineWorkerFetch 时,异步 drain 工作会自动绑定到 waitUntil;使用原始 { fetch } 处理程序时,请向 createWorkersLogger 传入 { executionCtx: ctx }。宽事件
逐步构建上下文,最后发射:
src/worker.ts
import { defineWorkerFetch, initWorkersLogger } from 'evlog/workers'
initWorkersLogger({
env: { service: 'my-worker' },
})
export default defineWorkerFetch(async (request, env, _ctx, log) => {
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 })
})
终端输出
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-...
错误处理
使用 createError 创建结构化错误,并使用 try/catch 进行处理:
src/worker.ts
import { createError, parseError } from 'evlog'
import { defineWorkerFetch, initWorkersLogger } from 'evlog/workers'
initWorkersLogger({ env: { service: 'my-worker' } })
export default defineWorkerFetch(async (request, env, _ctx, log) => {
try {
const body = await request.json()
log.set({ payment: { amount: body.amount } })
if (body.amount <= 0) {
throw createError({
status: 400,
message: '无效的支付金额',
why: '金额必须是正数',
fix: '以分为单位传入一个正整数',
})
}
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 })
}
})
配置
请参阅 配置参考 了解所有可用选项(initLogger、中间件选项、采样、静默模式等)。
排水和增强器
通过 initWorkersLogger 选项配置排水和增强器:
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 配置
禁用 Cloudflare 的默认调用日志以避免重复:
wrangler.toml
[observability]
enabled = false
本地运行
终端
wrangler dev