本文由 Huifer 撰写,TanStack Ship 的独立开发者与维护者。 我在 12 个以上运行于 Cloudflare Workers 的 TanStack Ship SaaS 应用中同时运行性能监控与产品分析——这些应用为数万月活用户提供服务,由我构建、出现问题时也会先通知我。分工如下:性能侧是边缘指标、结构化日志和 Tail Worker 追踪;产品分析侧是自托管 PostHog;两者通过从浏览器到 Worker 再到数据库的共享请求 ID 连接。本文中每个阈值、查询和告警都是我实际交付的。提前声明对 TanStack Ship 的偏向——这是我的付费产品。
经验证的信息来源: Cloudflare Workers Analytics Engine · Cloudflare Workers Observability · Web Vitals · OpenTelemetry · PostHog open-source product analytics · Google SRE Workbook — SLO chapter
最近更新:2026-07-12 · 更新日志
TL;DR: 性能监控与产品分析并非同一学科,但孤立地运行时失败方式相同——性能告诉你某个指标动了,用户却做了什么它不知道;分析告诉你用户做了什么,系统是否承载了他们的行为它不知道。我在 2026 年 Cloudflare Workers 上运行的统一技术栈包含五层性能指标加一层产品分析,通过请求 ID 关联,包装在 SLI 和 SLO 中,仅在用户可见的性能下降时触发告警。本文涵盖五层遥测栈、产品分析层、SLI/SLO 计算、三种应用规模的成本经济模型、仪表板,以及每周复盘机制。
为什么监控与分析需要放在同一个语境下
大多数 SaaS 团队将监控与分析作为两个独立的技术栈运行——一个用 Datadog,另一个用 Mixpanel——绘制两张仪表板,从不关联数据。结果是一幅碎片化的图景:每个问题你只能回答一半。
单独依赖监控的盲区
性能技术栈用指标、日志和追踪来回答「系统健康吗?」这个问题。它能检测到 /api/checkout 的延迟发生了回归、部署后 5xx 错误率飙升,或者某个数据库查询现在扫描的行数从 40 变成了 40,000。但它无法告诉你这次峰值是影响了一个用户还是一万个用户,是落在了转化路径上还是一个无人访问的管理后台,以及是否是新功能触发了这次回归。
我为此付出了昂贵的代价。2026 年第一季度,一次 TanStack Ship 计费模块 部署将 p95 结账延迟从 320ms 推高到了 780ms。告警触发了,回滚执行了,我以为一切良好——直到分析仪表板显示在回归上线的 4 个小时内,转化率下降了 11%。监控告诉我「出了问题」;但它无法告诉我「什么正处于风险之中」。
单独依赖分析的盲区
产品分析回答「用户做了什么?」——注册、激活、漏斗下降、留存、功能采用。它告诉你试用转付费转化上周下降了 4 个百分点。但它无法告诉你「为什么」——是升级流程中的 bug、一次跨越了可用性阈值的减速,还是上游 OAuth 服务中断。
我曾辅导过的一个 SaaS 团队靠纯产品分析运行了两个月。他们的转化率下降源于一次缓慢的 OAuth 握手过程,在首次登录时增加了 4 秒延迟。他们在 OAuth 路径上没有任何追踪数据,是通过阅读一条支持工单的相关日志才找到根因的。
「统一」的实际含义
统一并不意味着单一供应商。它意味着一个共享标识符——一个由浏览器发送、Worker 记录、分析事件携带、数据库查询附着的请求 ID。一旦同一个 ID 同时出现在两个仪表板中,你就能回答单靠任何一方都无法回答的问题。
实践中,就是在客户端生成一个 UUID,传播到每个服务端事件,写入 Analytics Engine 行、附加到 PostHog 事件、并标记到 D1 查询。以该 ID 关联后,两个技术栈就变成了一个。
我运行的五层性能技术栈
性能遥测分为五层。每一层都有其职责;没有任何一层可以替代另一层。
第一层:Workers Analytics Engine 上的边缘指标
聚合指标存储在 Workers Analytics Engine 上——一个面向高基数边缘负载的时间序列数据库:写入密集型、支持 SQL 查询、成本低。
-- 过去 6 小时内每条路由的 p95 延迟
SELECT
blob1 AS route,
int2 AS status_class,
quantileCont(0.95)(double4) AS p95_ms
FROM response_metrics
WHERE timestamp > NOW() - INTERVAL '6' HOUR
GROUP BY route, status_class
ORDER BY p95_ms DESC
LIMIT 25;
每个 Worker 在每次响应时写入一行,包含路由、状态类、耗时和维度列(部署 ID、地域、请求 ID)。每个应用一个仪表板,每 5 分钟拉取一次此查询。同样的数据为驱动告警的 SLI 计算提供输入。
第二层:Workers Logs 和 Tail Workers 中的结构化日志
指标告诉你一个数字动了;日志告诉你哪些请求中包含了这个数字。Workers Logs 捕获控制台输出和异常追踪,内置请求关联。我记录结构化 JSON,这样可以用 jq 搜索并导入事件复盘。
Tail Worker 保存每个请求的详细信息——已脱敏 PII 的请求日志、不含 Cookie 的请求头、正文形状而非正文值的请求体,以及将所有信息串联起来的请求 ID。默认采样率为 1/10;调试任何部署时提升到 100%。
第三层:通过 Tail Workers 和 OpenTelemetry 的分布式追踪
追踪回答「哪个数据库查询、在哪个下游调用中、在哪个部署上、导致了这次回归」。OpenTelemetry 模式在 Workers 上可用,但有一个注意事项——没有进程内代理,所以我从 Tail Worker 导出 OTLP。
// tail-worker.ts — 为每个采样请求发出一个 OTLP span
export default async function (events: TraceItem[], env: Env) {
const spans = events.map((e) => ({
traceId: e.requestId,
name: `${e.event.request.method} ${e.event.request.url}`,
startTimeUnixNano: e.event.cp,
endTimeUnixNano: e.event.cp + (e.event.wallTimeMs * 1_000_000),
attributes: {
'http.method': e.event.request.method,
'http.route': e.event.scriptName,
'http.status_code': e.event.response?.status,
'deploy.id': env.DEPLOY_ID,
},
}));
await fetch(env.OTLP_ENDPOINT, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ resourceSpans: [{ spans }] }),
});
}
我尚未在 5k spans/s 以上的持续负载下做过基准测试——在我操作的规模下这从未成为瓶颈。如果你超过了这个阈值,切换到采样 tail worker。
第四层:基于 web-vitals + beacon 的真实用户监控
服务端指标告诉你边缘看到了什么。RUM 告诉你用户设备实际经历了什么。两者会产生分歧——你的边缘可以在 80ms 内返回 200,但用户看到的是 6 秒的 LCP,因为第三方脚本阻塞了主线程。我的 RUM 指南 覆盖了完整实现;简言之就是浏览器中的 web-vitals,向 Worker 端点发送 sendBeacon,同一个请求 ID 流入 Analytics Engine 并标记 source=rum。
采样率是一个陷阱。100% 会烧光 CPU 和配额;1% 会失去对低流量租户的可见性。我在全球范围内采样 10%,在错误响应时采样 100%,在任何活跃事件期间采样 100%。
第五层:基于定时触发 Worker 的合成检查
RUM 只在真实用户访问路由时触发。合成检查则在无人关注时也会触发。定时触发的 Worker 每分钟从三个地域访问最关键的路径——登录、注册、升级流程、定价页。延迟回归首先在这里显现。
2025 年一次 D1 写入竞争事件中,我深刻认识到合成检查的价值:合成检查器比第一个客户影响早 90 秒通知了我。
让性能变得有意义的产品分析层
性能告诉你系统动了什么。产品分析告诉你哪个用户做了哪个动作。两者一起才能回答那个付账单的问题。
控制成本的事件收集
我在一台小型 VM 上自托管 PostHog。它捕获重要的事件——注册、激活、升级、流失、功能使用——并通过请求 ID 关联到性能数据。成本与月活跃用户数挂钩,而非事件数——这是 SaaS 的正确成本形态,每个用户产生数十个事件。
一个常见错误:全量埋点。我将客户端事件控制在约 12 个命名事件。原则是:如果一个事件不会改变我本会做出的决策,我就不收集它。
漏斗、留存和功能使用
三个分析视图在每个 SaaS 仪表板上都能发挥价值:
- 激活漏斗——从落地页到用户首次感知产品价值的 4 到 6 个步骤。下降 5% 即为回归。
- 队列留存——每个注册周的用户在第 N 周仍然活跃的百分比。这个曲线形状比任何单一指标都更能说明你的产品。
- 功能使用热力图——付费用户中接触过每个功能的比例。低于 5% 是移除候选;高于 40% 是加投候选。
将产品事件关联到性能追踪
这是统一技术栈得名的地方。每个 PostHog 事件都携带 request_id 属性。每个 Analytics Engine 行也携带同一个 ID。查找导致付费客户流失的慢速结账路径:
// 查找取消订阅用户的性能追踪
const slowCheckout = await analytics.query(`
SELECT * FROM response_metrics
WHERE timestamp > NOW() - INTERVAL '7' DAY
AND blob1 = '/api/checkout'
AND double4 > 500
AND blob3 IN (
SELECT distinct_id FROM posthog_events
WHERE event = 'subscription_cancelled'
AND timestamp > NOW() - INTERVAL '7' DAY
)
LIMIT 100;
`);
这个查询就是「上周结账变慢了」与「结账在取消订阅的队列中变慢了,这里是可以查看的请求 ID」的差距。它是单靠任何一方都无法回答的问题。
真正通知对的人的 SLI、SLO 和错误预算
没有 SLI 和 SLO,告警会对每个波动的指标都触发。有了它们,告警只会在用户感受到你承诺不会施加的痛苦时触发。
选择用户能感知的 SLI
SLI 是一个比率:好的事件数除以有效事件数。我追踪的四个 SLI:
- 可用性——2xx 响应数除以所有响应数,不含重定向。
- 延迟——300ms 以内的响应数除以所有响应数,按 p95 计算。
- 正确性——成功写入数除以所有写入尝试数。
- 新鲜度——成功的后台任务运行数除以计划运行数。
原则是「用户能感受到什么」。队列深度是信号;除非用户能感知到它,否则它不是 SLI。
经得起实践检验的 SLO 目标
一个达不到的目标是表演。一个太容易达到的目标不是契约。我运行的目标:
- 可用性:滚动 28 天窗口内 99.9%(每月约 40 分钟预算)
- 延迟:p95 下 95% 的请求在 300ms 以内
- 正确性:写入操作 99.99%
- 新鲜度:后台任务 99%
我按季度审视这些目标。消耗预算低于 50% 的应用是收紧的候选;消耗超过 80% 的是真正可靠性投入的候选。
告警路由与燃烧率告警
SLO 的意义在于错误预算——你在一个窗口内允许自己的糟糕程度。燃烧率告警在预算消耗速度不可持续时触发。我运行两个:
- 快速燃烧——1 小时内消耗 2% 预算。直接电话告警。
- 慢速燃烧——24 小时内消耗 5% 预算。Slack 通知,不电话。
在我运行的每个应用上,燃烧率告警已取代阈值告警。调优大约花了一个月,每季度的告警电话减少了约 4 倍。
成本经济模型:三种 SaaS 规模,两种技术栈
我被问到最多的问题是「这个技术栈实际成本是多少?」取决于规模,但成本形状是可预测的。
小型应用——MAU 低于 5 万
Cloudflare 原生技术栈基本上免费。Workers Analytics Engine 在付费 Workers 计划中每天包含 10 万个事件;$5/月的 VM 上自托管 PostHog 处理分析。同等规模下第三方技术栈——Datadog 加 Mixpanel——约 $80-200/月。免费技术栈的吸引力是明显的。
中型应用——MAU 5 万到 10 万
免费技术栈开始出现摩擦。Analytics Engine 写入量随规模线性增长;在 10 万 MAU 时很可能触及包含配额的限制,需要付费附加包或激进采样。总月运行成本:$30-200。
同规模第三方技术栈为 $400-1,500/月。Cloudflare 原生技术栈的经济账很扎实——但运营上的取舍是真实的。我有一款应用在这个量级使用第三方技术栈,因为自管理追踪的在岗负担不值得那些节省。
较大型应用——MAU 高于 10 万
到这个规模两种技术栈成本趋同,第三方技术栈通常在功能上胜出。Datadog APM、Honeycomb 的 BubbleUp、New Relic 的错误追踪是成熟产品,有多年投入。总月运行成本:$200-1,500(Cloudflare 原生)对比 $1,500-6,000(托管技术栈)。
托管可观测性技术栈胜出的场景
三个经验法则:
- 如果你的工程团队超过两人,Datadog 或 Honeycomb 的协作功能通常物有所值。
- 如果你的追踪量超过约 5,000 spans/s 持续负载,托管技术栈的索引管道比我自建的任何方案都更可靠。
- 如果你在多个云服务商上运营,第三方技术栈是获取统一视图的唯一途径。
对于在 Cloudflare Workers 上运行 SaaS 的独立开发者应用——这正是 TanStack Ship 的设计目标——Cloudflare 原生技术栈是更好的默认选择。
仪表板、告警路由和每周复盘
不看的遥测数据就是浪费的遥测数据。
三个仪表板,不是三十个
我运行的每个 TanStack Ship 应用有三个仪表板:
- 生产健康——p95 延迟、错误率、请求量、部署标记叠加。一屏可扫。
- 用户漏斗——注册 → 激活 → 升级 → 留存。周视图。
- SLO 状态——每个 SLI 当前 28 天错误预算剩余量,用绿/黄/红颜色编码。
其他一切都是下钻,不是顶级仪表板。如果一个回归没有出现在三个仪表板中的某一个上,它就不值得一个告警。
告警严重级别阶梯
四个级别:
| 严重级别 | 路由 | 示例 |
|---|---|---|
| P0 | 电话告警 | 快速燃烧 SLO 告警、完全中断 |
| P1 | Slack + 电话 | 持续错误率超标、部署回归 |
| P2 | Slack | 慢速燃烧 SLO 告警、单一路由延迟回归 |
| P3 | 邮件/摘要 | 容量警告、证书过期 |
我看到最常见的错误是 P1 过多。如果一切都是 P1,就没有 P1 了。阶梯迫使你回答一个问题「凌晨 3 点我真的会采取行动吗?」——如果答案是「等到早上再说」,那就不是 P1。
周一早晨复盘机制
每周一上午 9 点,我花 30 分钟回顾:
- 上周的 SLO 燃烧率
- 按用户影响排名前三的延迟回归
- 排名前三的转化漏斗变化
- 任何 P0/P1 事件及其事后分析状态
这就是整个机制。两年来我没有漏掉任何客户可见的回归——每周复盘捕获了告警遗漏的问题。
技术栈作为一个整体系统
监控与分析在孤立地运行时以同样的方式失败:每个只能回答每个问题的一半。通过请求 ID 关联,并以 SLI/SLO 作为契约,它们就变成了一个整体——只在用户可见的性能下降时告警,将遥测数据与业务成果关联起来,成本上是独立开发者的时间但不是托管平台预算。
我在 2026 年运行的技术栈并不另类。它是被凌晨 3 点告警太多次之后的无聊结论:五层性能、一层分析、一个共享标识符、SLO、三个仪表板,加一个周一早晨复盘。如果你在 Cloudflare Workers 上发布 SaaS 而没有运行接近这个的系统,TanStack Ship 样板 已经预置了遥测层——所以你从我不得不自己摸索的地方开始。
下一步: 阅读可观测性深度解析了解三支柱实现细节,阅读 RUM 指南了解客户端埋点,阅读分析技术栈设置了解我运行的 PostHog 配置。在决定哪个样板为你承载遥测之前,将 TanStack Ship 的监控默认值与 Shipfast 和 Makerkit 进行对比。