排查一条经过 Cloudflare 的请求,过去往往要在安全事件、缓存状态、Workers 日志和源站日志之间反复切换。Cloudflare Traces 的关键变化,是把请求经过安全规则、转换、缓存、路由、Workers 和源站的过程连接起来,并继续追踪它在其他服务中的流动。对开发团队而言,这比单独增加一份日志更重要:问题开始以“请求链路”而不是“产品页面”为单位呈现。
真正要解决的是跨层归因
一条请求可能在到达业务代码前就被多次处理。出现 403、缓存内容异常、请求头丢失或延迟突增时,仅查看源站通常无法回答这些问题:
- 请求是否被某条安全规则拦截或改变了处理路径?
- URL、请求头或其他属性是否经过转换?
- 返回内容来自缓存、Workers,还是源站?
- 路由是否把请求送到了预期服务?
- Workers 执行之后,请求发往了哪个后端?
- 延迟发生在边缘处理、源站,还是后续微服务中?
Cloudflare Traces 的价值在于将这些阶段放进同一条因果链。它不只是告诉你某个组件“发生过什么”,而是帮助确认前一个组件的决定如何影响后一个组件。
例如,源站没有收到请求,并不一定意味着源站故障。请求可能已在安全层结束,也可能由缓存直接响应。类似地,源站记录的路径与客户端发送的不一致,也可能是转换或 Workers 逻辑造成的。沿着链路观察,可以减少团队之间互相转交问题的时间。
阅读 Trace 时,不要只盯着耗时
调用链工具经常被当作性能瀑布图使用,但端到端排障还要检查请求状态如何变化。可以按下面的顺序阅读一次 Trace:
- 确认入口请求:核对方法、主机名、路径、时间范围和用于关联请求的标识。
- 检查安全决策:确认请求是否被允许继续,以及规则是否触发了挑战、拦截或其他动作。
- 比较转换前后状态:重点关注 URL、查询参数和请求头是否发生变化。
- 判断响应来源:区分缓存、Workers 和源站,避免在没有参与响应的系统中浪费时间。
- 检查路由与 Workers:确认请求进入了预期代码路径,并被转发到正确的后端。
- 继续追踪应用服务:请求离开边缘后,使用统一的关联标识检查网关、API、数据库访问层或异步任务。
这套顺序同时适用于错误和性能问题。对于延迟问题,应比较各阶段耗时;对于正确性问题,则应比较每个阶段前后的请求属性与决策结果。
可以这样实践:给源站日志补上关联 ID
下面是一个可改造的实践方案:在 Worker 中保留已有的 x-request-id,没有时生成一个,然后把它传给源站并写回响应头。这样,即使团队还在逐步接入完整链路追踪,也能用同一个 ID 对照客户端、Worker 和源站日志。
这只是通用的应用层关联方案,不代表 Cloudflare Traces 的内部字段或导出接口。若 Cloudflare Traces 提供了官方追踪上下文,应优先按照产品文档传播该上下文,而不是用自定义 ID 替换它。
创建 src/index.js:
export default {
async fetch(request, env) {
const incomingUrl = new URL(request.url);
const originUrl = new URL(env.ORIGIN_BASE_URL);
originUrl.pathname = incomingUrl.pathname;
originUrl.search = incomingUrl.search;
const headers = new Headers(request.headers);
const requestId = headers.get("x-request-id") || crypto.randomUUID();
headers.set("x-request-id", requestId);
const hasBody = request.method !== "GET" && request.method !== "HEAD";
const upstreamRequest = new Request(originUrl, {
method: request.method,
headers,
body: hasBody ? request.body : undefined,
redirect: "manual"
});
const upstreamResponse = await fetch(upstreamRequest);
const responseHeaders = new Headers(upstreamResponse.headers);
responseHeaders.set("x-request-id", requestId);
return new Response(upstreamResponse.body, {
status: upstreamResponse.status,
statusText: upstreamResponse.statusText,
headers: responseHeaders
});
}
};
对应的 wrangler.toml 可以这样写,运行前将示例域名改为自己的 HTTPS 源站:
name = "trace-correlation-demo"
main = "src/index.js"
compatibility_date = "2025-01-01"
[vars]
ORIGIN_BASE_URL = "https://origin.example.com"
部署并发起测试请求:
npx wrangler deploy
curl -i \
-H 'x-request-id: checkout-debug-001' \
'https://your-worker.example.com/api/orders/42'
源站应把这个字段写入结构化日志。下面的 Python 服务只使用标准库,可以直接运行,用于验证请求头是否成功传递:
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
request_id = self.headers.get("x-request-id", "missing")
print(json.dumps({
"request_id": request_id,
"method": self.command,
"path": self.path
}))
body = json.dumps({"ok": True, "request_id": request_id}).encode()
self.send_response(200)
self.send_header("content-type", "application/json")
self.send_header("content-length", str(len(body)))
self.end_headers()
self.wfile.write(body)
HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
本地启动并测试:
python origin.py
curl -i -H 'x-request-id: checkout-debug-001' http://127.0.0.1:8080/api/orders/42
生产环境中不要让客户端提供的任意 ID 直接成为可信安全字段。可以校验长度和字符集,或在边缘生成内部 ID,同时将外部 ID 作为单独字段保留。
一条适合团队执行的排障路径
当用户报告“结账接口偶发超时”时,可以采用下面的流程,而不是同时搜索所有日志:
- 固定一个可复现请求,记录准确时间、路径和关联 ID。
- 在 Trace 中确认请求是否经过了预期的安全、转换、缓存和路由阶段。
- 查看 Workers 是否调用了正确的源站,以及边缘阶段是否已经出现明显延迟。
- 进入源站和后续服务,用同一个关联 ID 搜索结构化日志。
- 找到最早出现状态异常或耗时突增的阶段,而不是只处理最后一个报错的组件。
- 修复后使用相同请求条件回归,比较修改前后的链路。
这种方法的重点是寻找“第一个偏离预期的节点”。下游错误经常只是上游错误的结果,例如错误路由可能最终表现为源站 404,而请求头转换错误可能最终表现为鉴权失败。
接入前需要划清的边界
端到端追踪会提高可观测性,也会带来新的治理要求:
- 敏感数据:不要默认记录完整 Cookie、Authorization、请求体或个人信息。
- 采样策略:高流量服务需要在成本与排障精度之间平衡;错误请求和关键交易可以采用更高采样率。
- 标识传播:跨服务调用、消息队列和后台任务都要明确如何传递追踪上下文。
- 日志基数:请求 ID 适合检索,但不适合直接作为监控指标标签,否则可能产生高基数问题。
- 时钟与异步边界:跨区域时钟偏差、重试和队列等待可能让时间线看起来不连续。
- 访问控制与保留期:Trace 可能暴露内部主机名、路径和规则决策,应限制访问并设置合理保留周期。
采用 Cloudflare Traces 时,可以先选一个同时经过安全规则、Workers 和源站的关键接口进行试点。验证团队能否用一条 Trace 回答“请求在哪里改变、在哪里结束、时间花在哪里”这三个问题,再扩大覆盖范围。工具本身提供的是链路,真正缩短故障时间的,是统一的关联标识、结构化日志和清晰的排障流程。