跳转到内容
搜索文档

可观察性 (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.exports。ctx 是 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 示例 ↗。

这篇文档对您有帮助吗?