前端监控:给用户体验装上仪表盘
线上问题用户先知道、开发最后知道,是没做监控时的常态。用户截图反馈"页面白屏了",你打开自己电脑一切正常,没有报错信息、没有复现路径,排查靠猜。前端监控就是解决这个局面的------把用户浏览器里发生的一切,变成你看得见的数据。
一套完整的前端监控覆盖四块:错误、性能、接口、行为。这篇按落地顺序讲。
错误监控:先把能抓的都抓上
JS 运行时错误靠 window.addEventListener('error') 捕获,Promise 未处理的拒绝靠 unhandledrejection:
window.addEventListener('error', (event) => { report({ type: 'js_error', message: event.message, filename: event.filename, lineno: event.lineno, colno: event.colno, stack: event.error?.stack, }); }); window.addEventListener('unhandledrejection', (event) => { report({ type: 'promise_error', reason: String(event.reason), }); });
资源加载失败(图片、脚本 404)同样走 error 事件,用 event.target 区分是元素错误还是脚本错误。接口错误在请求封装层统一拦截,状态码、耗时、接口地址一并上报。
有两类错误特别容易漏:跨域脚本报错时 window.onerror 只能拿到 "Script error." 一句话,需要在 script 标签加 crossorigin 属性、服务端配 CORS 头才能拿到完整堆栈;iframe 里的错误主页面监听不到,得在 iframe 内部单独埋。
SourceMap 还原是错误监控的灵魂 。线上代码是压缩过的,报错堆栈里的 a.is not a function 定位不了任何东西。构建时生成 sourcemap 上传到监控服务端(不部署到线上,避免暴露源码),上报的是压缩后堆栈,服务端还原成源码位置。这一步没做,错误监控的实用性直接砍半。
性能监控:盯 Core Web Vitals,别只看自己的体感
页面"感觉慢"是主观的,LCP、INP、CLS 三个指标把体验变成了可度量的数字。采集用 web-vitals 库:
import { onLCP, onINP, onCLS } from 'web-vitals'; const reportMetric = (metric) => { report({ type: 'web_vital', name: metric.name, value: metric.value, rating: metric.rating, // good / needs-improvement / poor page: location.pathname, }); }; onLCP(reportMetric); onINP(reportMetric); onCLS(reportMetric);
除了三大指标,资源加载耗时、接口瀑布图(页面发起的每个请求的时间线)也值得采。性能数据的价值在分布而不是均值------"P75 的 LCP 是 2.8 秒"比"平均 1.9 秒"有用得多,前者告诉你四分之一的用户体验不合格。
采集时带上环境维度:机型(低端机白名单)、网络类型、地区、浏览器版本。性能问题往往集中在特定人群,平均值会把他们稀释到看不见。
上报策略:别让监控本身成为性能问题
监控 SDK 处理不好会变成新的负担------用户操作一多,上报请求刷屏。几条实践原则:
批量 + 延迟 。日志先攒在内存队列,定时(比如 10 秒)或满 N 条批量发一次,用 navigator.sendBeacon 在页面卸载时兜底发送,这个 API 不阻塞页面关闭。
采样。PV/UV 类行为数据采 10% 足够看趋势,错误数据全量采集一个不漏。资源加载明细这种大体积数据,可以只在性能差的会话里采。
去重 。同一个错误在循环里触发几百次是常事,本地按 message + stack 首行 做指纹去重,带一个计数字段,一条搞定。
降级。监控 SDK 自己的异常必须吞掉(try-catch 包住所有逻辑),监控挂了把业务搞挂是最讽刺的事故。
聚合告警:从数据到行动
原始日志堆在那里没人看等于没做。要有聚合层把数据变成行动:
-
错误按指纹聚合成 issue,新增错误类型(首次出现的指纹)触发告警,已有 issue 突增量级异常也触发
-
性能指标按版本聚合对比,新版本 P75 劣化超过阈值拦截发布
-
接口成功率跌破线联动后端告警,前端视角的数据(用户真实遇到的失败)经常比服务端监控更早发现问题
告警通道接到 IM 群里,带上关键上下文:错误堆栈、影响用户数、关联版本、复现链接(error 页面 URL + 参数)。收到告警的人能在两分钟内判断严重程度,这才是一条合格的告警。
用户行为回放:排查的杀手锏
报错前用户做了什么?光有堆栈经常还原不出来。记录用户的操作序列(点击、输入、路由跳转、接口调用),错误发生时把最近 N 步一起上报:
const breadcrumbs = []; const maxBreadcrumbs = 30; document.addEventListener('click', (e) => { breadcrumbs.push({ type: 'click', target: e.target.tagName + '.' + (e.target.className || ''), ts: Date.now(), }); if (breadcrumbs.length > maxBreadcrumbs) breadcrumbs.shift(); }, { capture: true });
序列还原出来的信息量远超想象------"用户在表单页点了提交,接口 500,重试三次后报了空指针",问题定位直接从小时级降到分钟级。更进一步的录屏回放(rrweb)成本高一些,按需给重点页面开。
自建还是买
开源方案 Sentry 是事实上的默认选项,错误监控开箱即用,自部署版本免费。Web Vitals 和行为数据要自己接(Sentry 的 performance 功能商业版更完整)。
国内的话,各云厂商的前端监控产品(阿里 ARMS、腾讯 RUM)免去自运维成本,数据出境合规也省心。团队小、没专人运维监控体系,直接用托管服务;对数据自主性要求高或者量太大成本扛不住,再考虑自建(Sentry self-hosted 或 ClickHouse 自行聚合)。
监控体系的价值公式很朴素:发现问题的速度 × 定位问题的效率。SDK 埋点解决"发现",聚合告警和用户行为还原解决"定位"。两半都齐了,"用户先知道、开发最后知道"的日子才算结束。
领域:前端工程
关键词:前端监控、Core Web Vitals、SourceMap、错误上报、用户行为回放