跳转到内容
搜索文档

可观察性 (Observability)

最后更新 查看 MarkdownAgent 设置

Dynamic Workers 支持通过 console.log() 调用生成的日志,以及在执行期间捕获的异常和请求元数据。要访问这些日志,您可以附加一个 Tail Worker,这是一个在 Dynamic Worker 完成后运行的回调函数,它会传递其收集的所有日志、异常和元数据。

本指南将向您展示如何:

  • 存储 Dynamic Worker 日志,以便您可以搜索、过滤和查询它们
  • 在执行期间收集日志并实时返回它们,以用于开发和调试

使用 Tail Workers 捕获日志

要保存 Dynamic Worker 发出的日志,您需要捕获它们并将它们写入可以存储的地方。设置此项需要三个步骤:

  1. 在 loader Worker 上启用 Workers Logs 以便保存日志输出。
  2. 定义一个 Tail Worker,它从 Dynamic Worker 接收日志并将它们写入 Workers Logs。
  3. 在创建 Dynamic Worker 时将 Tail Worker 附加到它上面。

在 loader Worker 上启用 Workers Logs

通过将 observability 设置添加到 loader Worker 的 Wrangler 配置中来启用 Workers Logs。然而,Workers Logs 仅捕获来自 loader Worker 本身的日志输出。Dynamic Workers 是独立的,因此它们的 console.log() 调用不会自动包含在内。要将 Dynamic Worker 日志纳入 Workers Logs 中,您需要定义一个 Tail Worker,它接收来自 Dynamic Worker 的日志并将它们写入 loader Worker 的 Workers Logs 中。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "observability": {
    "enabled": true,
    "head_sampling_rate": 1
  }
}
[observability]
enabled = true
head_sampling_rate = 1

定义 Tail Worker

当 Dynamic Worker 运行时,运行时系统会收集其所有的 console.log() 调用、异常和请求元数据。默认情况下,这些日志在 Dynamic Worker 完成后会被丢弃。

要保留它们,您可以在 loader Worker 上定义一个 Tail Worker。Tail Worker 是一个带有 tail() 方法的类。您可以在这里编写代码,决定如何处理这些日志。运行时系统将在 Dynamic Worker 完成后调用此方法,并传入其在执行期间收集的所有内容。

tail() 内部,您通过调用带有 JSON 对象的 console.log() 将每个日志条目写入 Workers Logs。在每个条目中包含一个 workerId 字段,以便您可以分辨哪个 Dynamic Worker 生成了每条日志,并在以后使用它来按 Dynamic Worker 过滤和搜索日志。

import { WorkerEntrypoint } from "cloudflare:workers";

export class DynamicWorkerTail extends WorkerEntrypoint {
	async tail(events) {
		for (const event of events) {
			for (const log of event.logs) {
				console.log({
					source: "dynamic-worker-tail",
					workerId: this.ctx.props.workerId,
					level: log.level,
					message: log.message,
				});
			}
		}
	}
}

Tail Worker 从 this.ctx.props.workerId 读取 workerId。您将在下一步将 Tail Worker 附加到 Dynamic Worker 时设置此值。

由于 Tail Worker 定义在 loader Worker 中,它的 console.log() 输出会与 loader Worker 本身的日志一起保存到 Workers Logs 中。

将 Tail Worker 附加到 Dynamic Worker

在您创建 Dynamic Worker 时,在 tails 数组中传递 Tail Worker。这告诉运行时系统:在此 Dynamic Worker 完成后,将其收集的日志发送给您定义的 Tail Worker。

要引用您在上一步中定义的 DynamicWorkerTail 类,请使用 ctx.exportsctx 是 loader Worker 的 fetch(request, env, ctx) 处理程序中的第三个参数。ctx.exports 使您能够访问从 loader Worker 导出的类。因为 Dynamic Worker 在单独的上下文中运行,无法直接访问该类,所以您使用 ctx.exports.DynamicWorkerTail() 来创建一个参考,运行时系统可以将其连接到 Dynamic Worker。

您还需要告诉 Tail Worker 它是为哪个 Dynamic Worker 记录日志。因为 Tail Worker 独立于 loader Worker 的 fetch() 处理程序运行,所以它无法访问您的局部变量。要将信息传递给它,请在创建实例时使用 props 选项。props 是一个键值对的普通对象,在附加 Tail Worker 时设置,并且 Tail Worker 可以在运行时从 this.ctx.props 中读取它。在这种情况下,您传入 workerId,以便 Tail Worker 知道哪个 Dynamic Worker 生成了日志。

const worker = env.LOADER.get(workerId, () => ({
	mainModule: WORKER_MAIN,
	modules: {
		[WORKER_MAIN]: WORKER_SOURCE,
	},
	tails: [
		ctx.exports.DynamicWorkerTail({
			props: { workerId },
		}),
	],
}));

return worker.getEntrypoint().fetch(request);

实时返回日志

上述设置是为了稍后存储日志,但有时在进行实时开发时,您需要立即获取日志。挑战在于 Tail Worker 和 loader Worker 的 fetch() 处理程序是分开运行的。Tail Worker 拥有日志,但构建响应的却是 fetch() 处理程序。您需要一个共享的位置,Tail Worker 可以写入日志,而 fetch() 处理程序可以读取日志。

Durable Object 非常适合这项任务。Tail Worker 和 fetch() 处理程序都可以按名称查找相同的 Durable Object 实例。Tail Worker 在 Dynamic Worker 完成后将日志写入其中,而 fetch() 处理程序则将其读出并包含在响应中。

该模式的工作原理如下:

  1. fetch() 处理程序在运行 Dynamic Worker 之前在 Durable Object 中创建一个日志会话。
  2. Dynamic Worker 运行并生成日志。
  3. 在 Dynamic Worker 完成后,Tail Worker 将收集的日志写入同一个 Durable Object。
  4. fetch() 处理程序从 Durable Object 中读取日志,并在响应中返回它们。
import { exports } from "cloudflare:workers";

// 1. Create a log session before running the Dynamic Worker.
const logSession = exports.LogSession.getByName(workerName);
const logWaiter = await logSession.waitForLogs();

// 2. Run the Dynamic Worker.
const response = await worker.getEntrypoint().fetch(request);

// 3. Wait up to 1 second for the Tail Worker to deliver logs.
const logs = await logWaiter.getLogs(1000);

有关完整的工作实现,请参考 Dynamic Workers Playground 示例

这篇文档对您有帮助吗?