---
title: "Cloudflare Workers"
description: "宽事件、结构化错误和日志记录，适用于 Cloudflare Workers 和持久对象。"
canonical_url: "https://evlog.zhcndoc.com/integrate/frameworks/cloudflare-workers"
---
# Cloudflare Workers

> 宽事件、结构化错误和日志记录，适用于 Cloudflare Workers 和持久对象。

`evlog/workers` 适配器为 Cloudflare Workers 和 Durable Objects 添加检测功能，并提供携带 Cloudflare 特定上下文的请求范围日志记录器。

使用 **withEvlog** 获取与其他框架集成相同的中间件管道：路由过滤、脱敏、增强、尾部采样、插件、排水和自动发射。如果你希望自行控制发射，则使用 `defineWorkerFetch` 或 `createWorkersLogger`。

<prompt :actions="["copy","cursor","claude"]" description="在我的 Cloudflare Worker 中设置 evlog" icon="i-simple-icons-cloudflare">

在我的 Cloudflare Worker 中设置 evlog。

- 安装 evlog：pnpm add evlog
- 从 'evlog/workers' 导入 initWorkersLogger 和 withEvlog
- 创建 evlog.config.ts，其中包含 export default defineEvlog({ service: 'my-worker' })
- 在顶层调用 initWorkersLogger(toLoggerConfig(config))，并将 toMiddlewareOptions(config) 传给 withEvlog
- 使用 **withEvlog** 包装 fetch 处理程序（推荐）。它会为你发射广泛事件，并接受 include/exclude、routes、redact、enrich、keep、plugins 和 drain
- 使用 log.set() 在整个请求过程中累积上下文
- 只有当你想自行调用 log.emit() 时，才使用 defineWorkerFetch 或 createWorkersLogger

文档：[https://www.evlog.dev/integrate/frameworks/cloudflare-workers](https://www.evlog.dev/integrate/frameworks/cloudflare-workers)
适配器：[https://www.evlog.dev/integrate/adapters/overview](https://www.evlog.dev/integrate/adapters/overview)

</prompt>

## 快速开始

### 1. 安装

<code-group>

```bash [pnpm]
pnpm add evlog
```

```bash [bun]
bun add evlog
```

```bash [yarn]
yarn add evlog
```

```bash [npm]
npm install evlog
```

</code-group>

### 2. 创建 evlog.config.ts

```typescript [evlog.config.ts]
import { defineEvlog } from 'evlog'

export default defineEvlog({
  service: 'my-worker',
})
```

所有选项都位于此文件中：采样、drain、`enrich` 和路由过滤器。`npx evlog init` 会为你写入它，以及下方的接入代码。请参阅[配置](https://evlog.zhcndoc.com/reference/configuration)。

### 3. 包装你的 fetch 处理程序

```typescript [src/worker.ts]
import { toLoggerConfig, toMiddlewareOptions } from 'evlog'
import { initWorkersLogger, withEvlog } from 'evlog/workers'
import config from '../evlog.config'

initWorkersLogger(toLoggerConfig(config))

export default withEvlog(async (request, _env, _ctx, log) => {
  log.set({ action: 'handle_request' })

  // ... 你的处理程序逻辑

  return Response.json({ ok: true })
}, toMiddlewareOptions(config))
```

`withEvlog` 会在处理程序返回时为每个请求发射一个宽事件，无需手动调用 `log.emit()`。它会从第三个参数中读取 `ExecutionContext`，因此异步的 **drain** 调用（PostHog、Axiom、……）会通过 `waitUntil` 注册，并在响应返回后保持活动状态。流式响应会将发射推迟到正文完成之后。

当调用方发送 `x-request-id` 时，`requestId` 会从中获取；否则回退到 `cf-ray`。`method`、`path`、`cf-ray`、`traceparent` 以及 `request.cf` 中的安全子集会被自动捕获。

### 选项

`withEvlog` 接受与其他框架集成相同的选项。将它们放在配置文件中：

```typescript [evlog.config.ts]
import { defineEvlog } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'

export default defineEvlog({
  service: 'my-worker',
  drain: createAxiomDrain(),
  exclude: ['/health'],
  routes: { '/api/**': { service: 'api' } },
  redact: true,
  enrich: (ctx) => {
    ctx.event.colo = ctx.event.colo ?? 'unknown'
  },
  keep: (ctx) => {
    if (ctx.duration > 1000) ctx.shouldKeep = true
  },
})
```

`initWorkersLogger` 始终会将 `pretty` 和 `stringify` 设置为 `false`，无论文件中如何设置。该文件会与 Worker 一起打包并在那里求值，因此其中的 `process.env` 读取只有在 Cloudflare 通过 `process.env` 暴露 Worker 变量时才可见（需要 `nodejs_compat` 标志）。`createAxiomDrain()` 等适配器也以相同方式读取密钥。

### 手动发送

想要自行控制事件发送？`defineWorkerFetch` 会为你连接 `ExecutionContext`，但将 `log.emit()` 留给你手动调用：

```typescript [src/worker.ts]
import { toLoggerConfig } from 'evlog'
import { defineWorkerFetch, initWorkersLogger } from 'evlog/workers'
import config from '../evlog.config'

initWorkersLogger(toLoggerConfig(config))

export default defineWorkerFetch(async (request, _env, _ctx, log) => {
  log.set({ action: 'handle_request' })
  log.emit()
  return Response.json({ ok: true })
})
```

<callout color="info" icon="i-lucide-info">

`defineWorkerFetch` 和 `createWorkersLogger` 是底层路径：它们会创建日志记录器，并将生命周期管理留给你，因此 `include` / `exclude`、`routes`、`redact`、`enrich`、`keep` 和 `plugins` **不会**生效。使用 `withEvlog` 即可获得这些功能。

</callout>

## 宽事件

逐步构建上下文，最后发射：

```typescript [src/worker.ts]
import { toLoggerConfig } from 'evlog'
import { defineWorkerFetch, initWorkersLogger } from 'evlog/workers'
import config from '../evlog.config'

initWorkersLogger(toLoggerConfig(config))

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 })
})
```

```bash [终端输出]
14:58:15 INFO [my-worker] GET /api/users 200 耗时 12ms
  ├─ orders: count=5
  ├─ user: id=usr_123 plan=pro
  ├─ route: /api/users
  └─ requestId: 4a8ff3a8-...
```

## 错误处理

使用 `createError` 创建结构化错误，并使用 try/catch 进行处理：

<code-collapse>

```typescript [src/worker.ts]
import { createError, parseError } from 'evlog'
import { toLoggerConfig } from 'evlog'
import { defineWorkerFetch, initWorkersLogger } from 'evlog/workers'
import config from '../evlog.config'

initWorkersLogger(toLoggerConfig(config))

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 })
  }
})
```

</code-collapse>

## 配置

请参阅 [配置参考](https://evlog.zhcndoc.com/reference/configuration) 了解所有可用选项（`initLogger`、中间件选项、采样、静默模式等）。所有选项都位于 `evlog.config.ts` 中。

## 排水和增强器

将 drain 和增强器放入文件中。`toLoggerConfig` 会将 drain 传给 `initWorkersLogger`，`toMiddlewareOptions` 会将两者传给 `withEvlog`，后者会在每个请求事件上运行它们：

```typescript [evlog.config.ts]
import { defineEvlog, type DrainContext } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'
import { createUserAgentEnricher } from 'evlog/enrichers'
import { createDrainPipeline } from 'evlog/pipeline'

const pipeline = createDrainPipeline<DrainContext>({
  batch: { size: 50, intervalMs: 5000 },
})
const userAgent = createUserAgentEnricher()

export default defineEvlog({
  service: 'my-worker',
  drain: pipeline(createAxiomDrain()),
  enrich: (ctx) => {
    userAgent(ctx)
  },
})
```

<callout color="info" icon="i-lucide-info">

请参阅 [适配器](https://evlog.zhcndoc.com/integrate/adapters/overview) 和 [增强器](https://evlog.zhcndoc.com/use-cases/enrichers) 文档，了解所有可用的排水适配器和增强器。

</callout>

## Wrangler 配置

禁用 Cloudflare 的默认调用日志以避免重复：

```toml [wrangler.toml]
[observability]
enabled = false
```

## 本地运行

```bash [终端]
wrangler dev
```

## 下一步

- [宽事件](https://evlog.zhcndoc.com/learn/wide-events): 通过上下文分层设计全面的事件
- [适配器](https://evlog.zhcndoc.com/integrate/adapters/overview): 将日志发送到 Axiom、Sentry、PostHog 等
- [采样](https://evlog.zhcndoc.com/learn/sampling): 使用头部和尾部采样控制日志量
- [结构化错误](https://evlog.zhcndoc.com/learn/structured-errors): 抛出包含 `why`、`fix` 和 `link` 字段的错误。


## Sitemap

See the full [sitemap](https://evlog.zhcndoc.com/sitemap.md) for all pages.
