Cloudflare Workers 自动插桩 fetch 调用、KV 读取和 D1 查询等平台操作。自定义 span 让您将可见性扩展到自己的应用程序逻辑,以便在内置插桩的同时追踪自定义代码路径。
自定义 span API 有两种使用方式——两者提供相同的 enterSpan() 方法,行为完全一致:
import { tracing } from "cloudflare:workers"— 可在代码库任意位置使用,包括工具函数、库以及无法访问处理程序上下文的模块。ctx.tracing— 在传递给处理程序的ExecutionContext上可用,在处理程序内部工作时更为便捷。
自定义 span 需要在 Worker 上启用追踪。如果尚未启用,请在 Wrangler 配置文件 中将 observability.traces.enabled 设置为 true:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"observability": {
"traces": {
"enabled": true
}
}
}[observability.traces]
enabled = true使用 tracing.enterSpan() 将一段代码包装在命名 span 中。span 会自动成为当前活动 span 的子 span,并在回调返回或其返回的 promise 完成时结束。
以下示例同时使用两种访问方式——cloudflare:workers 导入和 ctx.tracing——以表明两者可以互换:
import { tracing } from "cloudflare:workers";
export default {
async fetch(request, env, ctx) {
// Using the import
return tracing.enterSpan("handleRequest", async (span) => {
span.setAttribute("url.path", new URL(request.url).pathname);
const user = await ctx.tracing.enterSpan("auth", async () => {
// Using ctx.tracing
return authenticate(request, env);
});
return buildResponse(user);
});
},
};import { tracing } from "cloudflare:workers";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// Using the import
return tracing.enterSpan("handleRequest", async (span) => {
span.setAttribute("url.path", new URL(request.url).pathname);
const user = await ctx.tracing.enterSpan("auth", async () => {
// Using ctx.tracing
return authenticate(request, env);
});
return buildResponse(user);
});
},
};创建新 span 并在其中运行 callback。当回调返回(同步或异步)或抛出异常时,span 自动结束。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
name |
string |
span 的名称。会显示在追踪可视化中。 |
callback |
(span: Span, ...args: A) => T |
在 span 内执行的函数。第一个参数接收 Span 对象,后续为传递给 enterSpan 的额外参数。 |
...args |
A |
可选的额外参数,在 span 参数之后转发给回调。 |
返回值: callback 的返回值。
行为:
- 新 span 是当前异步上下文上活动 span 的子 span。如果没有活动 span,则成为请求根 span 的子 span。
- 在回调内运行的嵌套
enterSpan调用和运行时创建的 span(如fetch或 KV 操作)会自动成为此 span 的子 span。 - 当回调同步返回、同步抛出异常,或其返回的 promise 完成或拒绝时,span 结束。
// Synchronous callback — span ends when the function returns
const result = tracing.enterSpan("parse", (span) => {
span.setAttribute("format", "json");
return JSON.parse(body);
});
// Async callback — span ends when the promise settles
const data = await tracing.enterSpan("fetchData", async (span) => {
const res = await fetch("https://api.example.com/data");
span.setAttribute("http.response.status_code", res.status);
return res.json();
});
// Forwarding arguments
const doubled = tracing.enterSpan("compute", (span, x) => x * 2, 21);Span 对象传递给 enterSpan 回调。它提供向 span 添加元数据的方法。
在 span 上设置 attribute。
| 参数 | 类型 | 说明 |
|---|---|---|
key |
string |
attribute 名称。 |
value |
string | number | boolean | undefined |
attribute 值。传入 undefined 时不执行任何操作。 |
Attributes 会随 span 一起显示在追踪和 OpenTelemetry 导出中。
span.setAttribute("user.plan", "enterprise");
span.setAttribute("item.count", 42);
span.setAttribute("cache.hit", true);一个 readonly boolean,表示此次调用是否正在被追踪。当请求未被采样(基于您的 head_sampling_rate)时,isTraced 为 false,enterSpan 仍会运行回调但不会记录任何遥测数据。
您可以用此在请求未被追踪时跳过昂贵的 attribute 计算:
tracing.enterSpan("process", (span) => {
if (span.isTraced) {
span.setAttribute("request.body.preview", JSON.stringify(body).slice(0, 200));
}
return processBody(body);
});Span 根据 JavaScript 异步上下文自动嵌套。在回调内运行的任何 enterSpan 调用或平台操作(如 fetch、env.MY_KV.get() 等)都会成为外层 span 的子 span。
import { tracing } from "cloudflare:workers";
async function handleOrder(env, orderId) {
return tracing.enterSpan("handleOrder", async (span) => {
span.setAttribute("order.id", orderId);
// This KV read is automatically a child of "handleOrder"
const order = await env.ORDERS_KV.get(orderId, "json");
// This nested span is also a child of "handleOrder"
const total = tracing.enterSpan("calculateTotal", (innerSpan) => {
innerSpan.setAttribute("item.count", order.items.length);
return order.items.reduce((sum, item) => sum + item.price, 0);
});
// This fetch is a child of "handleOrder"
await fetch("https://api.example.com/notify", {
method: "POST",
body: JSON.stringify({ orderId, total }),
});
return new Response(JSON.stringify({ orderId, total }));
});
}import { tracing } from "cloudflare:workers";
async function handleOrder(env: Env, orderId: string) {
return tracing.enterSpan("handleOrder", async (span) => {
span.setAttribute("order.id", orderId);
// This KV read is automatically a child of "handleOrder"
const order = await env.ORDERS_KV.get(orderId, "json");
// This nested span is also a child of "handleOrder"
const total = tracing.enterSpan("calculateTotal", (innerSpan) => {
innerSpan.setAttribute("item.count", order.items.length);
return order.items.reduce((sum: number, item: any) => sum + item.price, 0);
});
// This fetch is a child of "handleOrder"
await fetch("https://api.example.com/notify", {
method: "POST",
body: JSON.stringify({ orderId, total }),
});
return new Response(JSON.stringify({ orderId, total }));
});
}
console.log() 和其他 console 方法会发出自动归属于当前活动 span 的日志事件。这意味着在 enterSpan 回调内输出的日志会与该 span 关联,显示在追踪和 OpenTelemetry 导出中。
tracing.enterSpan("processPayment", async (span) => {
console.log("Starting payment processing"); // attributed to "processPayment"
const result = await chargeCard(token, amount);
console.log("Payment complete", result.id); // also attributed to "processPayment"
});自定义 span API 的完整类型声明:
declare module "cloudflare:workers" {
namespace tracing {
function enterSpan<T, A extends unknown[]>(
name: string,
callback: (span: Span, ...args: A) => T,
...args: A
): T;
}
class Span {
readonly isTraced: boolean;
setAttribute(
key: string,
value: string | number | boolean | undefined,
): void;
}
}相同的 API 在处理程序上下文中以 ctx.tracing 提供,类型相同。
- 不支持手动管理 span 生命周期。 Span 始终限定在
enterSpan回调范围内。无法启动 span 并在稍后结束。 - 不支持手动设置父子关系。 父子关系由 JavaScript 异步上下文自动确定。
- 尚不支持
setAttributes(批量设置)。 请使用单独的setAttribute调用。批量设置计划在未来版本中提供。 - 尚不支持
spanContext()(trace/span ID)。 跨边界手动传播所需的 trace 和 span 标识符访问计划在未来版本中提供。 - 尚不支持
setOutcome。 设置 span 结果状态计划在未来版本中提供。
其他追踪限制请参阅已知限制页面。