跳转到内容
搜索文档

自定义 span

最后更新 查看 MarkdownAgent 设置

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

创建自定义 span

使用 tracing.enterSpan() 将一段代码包装在命名 span 中。span 会自动成为当前活动 span 的子 span,并在回调返回或其返回的 promise 完成时结束。

以下示例同时使用两种访问方式——cloudflare:workers 导入和 ctx.tracing——以表明两者可以互换:

src/index.jsjs
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);
		});
	},
};
src/index.tsts
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);
    });
  },
};

API 参考

tracing.enterSpan(name, callback, ...args)

创建新 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

Span 对象传递给 enterSpan 回调。它提供向 span 添加元数据的方法。

span.setAttribute(key, value)

在 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);

span.isTraced

一个 readonly boolean,表示此次调用是否正在被追踪。当请求未被采样(基于您的 head_sampling_rate)时,isTracedfalseenterSpan 仍会运行回调但不会记录任何遥测数据。

您可以用此在请求未被追踪时跳过昂贵的 attribute 计算:

tracing.enterSpan("process", (span) => {
  if (span.isTraced) {
    span.setAttribute("request.body.preview", JSON.stringify(body).slice(0, 200));
  }
  return processBody(body);
});

嵌套 span

Span 根据 JavaScript 异步上下文自动嵌套。在回调内运行的任何 enterSpan 调用或平台操作(如 fetchenv.MY_KV.get() 等)都会成为外层 span 的子 span。

src/index.jsjs
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 }));
	});
}
src/index.tsts
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 }));
  });
}
追踪瀑布图,显示自定义 span 与自动 KV 和 fetch 插桩嵌套在一起

在 span 内记录日志

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"
});

TypeScript 类型

自定义 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 结果状态计划在未来版本中提供。

其他追踪限制请参阅已知限制页面。

这篇文档对您有帮助吗?