前端监控搭建踩坑
今年年初老板说要搞前端监控,我就开始折腾。现在上线几个月了,记录一下。
需求
我们要监控这些东西:
- JS 报错
- 接口异常
- 页面性能
- 用户行为(可选)
方案选择
几个主流方案:
- Sentry:功能全,但收费,自建部署复杂
- 自研:灵活,但工作量大
- 阿里云 ARMS:国内访问快,但有点贵
最后选了 Sentry 自建版本,主要是团队有运维资源。
采集端
错误采集
// 全局错误
window.onerror = function(message, source, lineno, colno, error) {
reportError({
type: 'js_error',
message,
stack: error?.stack,
url: window.location.href,
});
};
// Promise unhandledrejection
window.addEventListener('unhandledrejection', (event) => {
reportError({
type: 'promise_error',
message: event.reason,
});
});
有个坑:压缩后的代码报错,行号对不上。需要上传 source map 到 Sentry。
性能采集
const timing = performance.getEntriesByType('navigation')[0];
const paint = performance.getEntriesByType('paint');
const metrics = {
// 首次渲染
FP: paint.find(e => e.name === 'first-paint')?.startTime,
// 首次内容渲染
FCP: paint.find(e => e.name === 'first-contentful-paint')?.startTime,
// DOM 加载完成
DCL: timing.domContentLoadedEventEnd - timing.domContentLoadedEventStart,
// 页面完全加载
Load: timing.loadEventEnd - timing.loadEventStart,
};
现在新的 API 是 PerformanceObserver,支持更多指标:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
console.log('LCP:', entry.startTime);
}
}
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
Source Map 问题
这是最大的坑。
生产环境代码是压缩过的,报错信息基本没用。需要上传 source map 到监控平台。
我们用 webpack 插件:
const { SentryWebpackPlugin } = require('@sentry/webpack-plugin');
module.exports = {
plugins: [
new SentryWebpackPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: 'my-org',
project: 'my-project',
include: './dist',
}),
],
};
注意:source map 不要部署到生产服务器!会暴露源代码。只在构建时上传到 Sentry,然后删掉。
告警配置
一开始我给所有错误都配了告警,结果手机响个不停。后来改成:
- 错误率超过阈值告警
- 同一错误超过 N 次告警
- 新出现的错误类型告警
还要配置静默时段,不然半夜被叫起来看日志很痛苦。
收获
上线几个月,发现了几个之前没注意到的 Bug:
- 某个旧版 Safari 的兼容问题
- 一个接口偶发性超时
- 第三方脚本加载失败导致页面卡死
这些东西光靠测试是测不出来的,线上监控确实有必要。
建议
- 先想清楚要监控什么,别上来就全量采集
- 告警要克制,太多了会麻木
- 有监控只是第一步,更重要的是跟进处理
我们现在每周看一次监控报告,整理出需要修复的问题,逐步迭代。