前端错误监控
面试题:如何搭建一套前端错误监控?
完整的前端监控分三块:错误监控、性能监控、行为监控(埋点)。本篇聚焦错误监控,埋点见 埋点上报。
错误捕获手段
| 错误类型 | 捕获方式 |
|---|---|
| JS 运行时错误 | window.onerror / window.addEventListener('error') |
| 资源加载错误(img/script/css) | window.addEventListener('error', cb, true)(捕获阶段,资源错误不冒泡) |
| Promise 未捕获 rejection | window.addEventListener('unhandledrejection') |
| 框架内错误 | Vue:app.config.errorHandler;React:ErrorBoundary + componentDidCatch |
| 跨域脚本错误 | <script crossorigin> + 服务端 CORS 头,否则只报 Script error. |
| 接口错误 | 封装 fetch/axios 拦截器统一上报非 2xx |
// JS 错误 + 资源错误(第三个参数 true 走捕获阶段)
window.addEventListener(
"error",
(event) => {
if (event.target && (event.target.src || event.target.href)) {
report({ type: "resource", url: event.target.src || event.target.href });
} else {
report({
type: "js",
message: event.message,
filename: event.filename,
line: event.lineno,
col: event.colno,
stack: event.error?.stack,
});
}
},
true
);
// Promise 错误
window.addEventListener("unhandledrejection", (event) => {
report({ type: "promise", reason: event.reason?.message || event.reason });
});
sourcemap 还原
面试题:上报的报错堆栈是压缩混淆后的,怎么定位源码?
生产代码经过压缩,堆栈里的行列号无意义。做法:构建时生成 .map 文件但不部署到线上(上传到监控平台),上报时带上压缩后的行列号,平台端用 source-map 库还原成源码位置。Sentry 就是这么做的。
上报要点
- 上报通道:优先
navigator.sendBeacon(页面卸载也能发),降级用new Image().src(无跨域预检)或fetch(keepalive: true)。 - 去重与采样:相同错误聚合(按 message+stack 生成指纹),高频错误采样上报,避免打爆服务端。
- 限流:单页面错误数设上限。
- 附加上下文:用户 id、设备/浏览器、页面 url、用户操作录屏(如 rrweb)、面包屑(最近的几次操作)。
性能监控(关联)
用 PerformanceObserver + web-vitals 采集核心指标:
- LCP(最大内容绘制)、INP(交互到下一次绘制,已取代 FID)、CLS(累积布局偏移)。
- 首屏时间、白屏时间、资源加载耗时(
performance.getEntriesByType('resource'))、接口耗时。
import { onLCP, onINP, onCLS } from "web-vitals";
onLCP(report);
onINP(report);
onCLS(report);
成熟方案:Sentry、阿里 ARMS、字节 Argos,自研可参考其架构。