写在前面
很多团队依赖后端日志来排查前端问题,这是隔靴搔痒。 后端接口 200 OK,不代表前端渲染成功;CDN 资源 404,后端根本感知不到;JS 逻辑报错,后端更是一脸懵逼。
真正的 前端监控 (RUM - Real User Monitoring) 必须运行在浏览器端。
市面上有 Sentry、Datadog 等成熟产品,为什么架构师还要懂怎么从 0 到 1 搭建?
- 成本: SAAS 真的很贵,流量大了买不起。
- 定制: 你需要结合业务数据(如:用户等级、订单 ID)进行深度分析。
- 原理: 理解了原理,你才能更好地使用工具,而不是被工具限制。
一、 系统架构:数据的旅程
一个完整的 APM 系统,其数据流向可以概括为四个阶段:
- 采集 (Collection): 埋伏在浏览器里的 SDK,负责监听一切。
- 上报 (Reporting): 也就是“如何把数据运出去”,不仅要快,还不能阻塞业务。
- 清洗与存储 (Processing): 原始数据是乱七八糟的,需要解析 UserAgent、映射 SourceMap。
- 消费 (Visualization): 报警大屏、性能趋势图、错误堆栈还原。
二、 采集层:SDK 的核心设计
SDK 是监控系统的“探头”。作为架构师,设计 SDK 时要遵循 “无侵入、低开销” 的原则。
2.1 性能采集:PerformanceObserver
别再用 performance.timing 了,那个 API 已经过时且精度不够。 现代监控 SDK 必须使用 PerformanceObserver API,它可以被动订阅各种性能事件。
// 核心代码:订阅 LCP 和 CLS
const observer = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
console.log('LCP:', entry.startTime);
// report(entry.startTime)
}
if (entry.entryType === 'layout-shift' && !entry.hadRecentInput) {
console.log('CLS:', entry.value);
// accumulateCLS(entry.value)
}
}
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
observer.observe({ type: 'layout-shift', buffered: true });
架构要点: 加上 buffered: true 很重要,否则你会漏掉 SDK 初始化之前发生的性能指标。
2.2 错误捕获:不仅仅是 try-catch
前端错误分三种,SDK 需要全覆盖:
- JS 运行时错误:
window.onerror。 - Promise 未捕获异常:
window.onunhandledrejection(这是现代应用最常见的白屏原因)。 - 资源加载失败:
window.addEventListener('error', handler, true)。注意要在捕获阶段监听,因为资源错误不冒泡。
2.3 行为回溯:还原案发现场
当报错发生时,只有堆栈是不够的。我们需要知道用户在报错前干了什么。 SDK 需要默认劫持(Hook)用户的交互行为,维护一个“行为队列(Breadcrumbs)”:
- 用户点击了哪个 DOM?
- 用户路由跳到了哪?
- 用户发起过哪个 API 请求?
当报错发生时,把这个队列一并上报。这样你就能复现:“哦,原来是先点了A,再点了B,最后接口 500 导致 JS 崩了。”
三、 上报层:优雅的数据传输
监控数据虽然重要,但它是次要任务。绝不能因为上报监控数据,导致业务请求变慢。
3.1 黄金通道:navigator.sendBeacon
这是一个专门为分析数据设计的 API。
- 特点: 即使页面关闭(Unload),数据也能发出去。
- 优势: 不占用主线程,不会阻塞页面跳转。
window.addEventListener('unload', () => {
const data = JSON.stringify(metricsQueue);
navigator.sendBeacon('/api/report', data);
});
3.2 闲时上报:requestIdleCallback
对于非紧急数据(如性能打点),不要产生 HTTP 请求风暴。 利用 requestIdleCallback,等到浏览器空闲的时候再打包发送。
3.3 抽样策略 (Sampling)
如果你的网站 PV 有 1 个亿,全量上报会让后端数据库原地爆炸。 架构师需要在 SDK 端设计抽样逻辑:
- 性能数据: 抽样 1% - 10% 即可,足以反映趋势。
- 错误数据: 必须 100% 上报(或者对同一种错误进行客户端聚合去重)。
四、 处理层:让数据说人话
后端收到的数据是压缩混淆过的代码堆栈,比如 at a.b (app.min.js:1:500)。这对于开发者来说毫无意义。
4.1 SourceMap 还原服务
你需要搭建一个内部服务(通常结合 CI/CD):
- 构建时: Webpack/Vite 打包生成
.map文件。 - 上传时: 将
.map文件上传到监控系统的私有存储(绝不能上传到公网 CDN! )。 - 解析时: 监控系统收到报错
line 1, col 500,去私有存储里找对应的 map 文件,通过mozilla/source-map库还原出src/components/Header.vue的第 25 行。
4.2 智能聚合
同一个 Bug 可能触发 10 万次报错。监控系统必须具备指纹算法 (Fingerprinting) ,将堆栈信息相似的错误聚合为一个 Issue,否则报警群会被消息淹没。
五、 实战建议:自研还是购买?
这是架构师经常面临的决策。
方案 A:购买 SaaS (Sentry / Datadog / 阿里云 ARMS)
- 优点: 功能强大,部署简单,支持 SourceMap 管理。
- 缺点: 贵。数据敏感性问题。
- 适用: 初创团队,快速验证,预算充足。
方案 B:开源自建 (Sentry On-Premise)
- 优点: 代码免费,功能同 SaaS。
- 缺点: Sentry 的部署维护极其复杂(依赖 Kafka, Redis, ClickHouse 等一堆组件),需要专门的运维支持。
方案 C:轻量级自研 (SDK + Node + ClickHouse/ES)
- 优点: 完全贴合业务,成本最低。
- 缺点: 需要开发资源,初期功能简陋。
- 适用: 中大型团队,有定制化需求(如需要关联内部的用户行为路径)。
架构师建议: 如果你是中小团队,直接接入 Sentry (SaaS) 是 ROI 最高的选择。不要为了造轮子而造轮子。 如果你是大型互联网公司,通常会基于 Web Vitals 库 + 自研 SDK + 大数据平台 来构建,因为数据量太大,且需要和业务数据打通。
结语:不仅是看,更是感知
搭建好了 APM,我们就像拥有了“千里眼”。 我们可以自信地告诉老板:“昨天下午 3 点,北京地区电信网络的用户,LCP 升高了 20%,原因是 CDN 节点故障。”而不是含糊地说:“好像网络有点抖。”
既然有了眼睛,看到了问题,接下来就要动手解决了。 除了之前学过的代码层面的优化,架构师还需要建立一套分层级的优化战术,从协议层到渲染层,全面围剿性能瓶颈。