1. 为什么前端监控是开发者的"保命符"
去年团队里有个同事负责的电商大促页面上线后,支付成功率突然跌了40%。排查三天才发现是某个边缘地区的用户因为CDN节点异常,导致核心JS文件加载失败。这种问题如果有完善的监控体系,本可以10分钟内自动告警并定位。这就是为什么我把前端监控称为"保命系统"——它能在你睡觉时盯着线上环境,在用户投诉前把问题揪出来。
现代前端监控主要解决两类核心问题:性能瓶颈和运行时错误。性能监控帮你发现"慢在哪里",错误监控告诉你"为什么崩"。两者结合就像给应用装了CT机和心电图,任何异常都能第一时间捕获。
2. 监控系统设计四要素
2.1 数据采集:该监控什么?
基础指标必须包含:
- 页面性能:FP/FCP/LCP等Web Vitals核心指标
- 资源加载:JS/CSS/图片的加载成功率和耗时
- API请求:成功率、响应时间、重试情况
- JS错误:运行时错误、Promise rejection
- 用户行为:关键操作路径、页面停留时长
我们在项目中用PerformanceObserver捕获性能指标,用window.onerror处理全局错误。特别注意要给所有异步请求包裹try-catch,因为这类错误window.onerror捕获不到。
2.2 传输策略:如何高效上报?
切忌有错就报,这会导致:
- 高频请求可能被浏览器限制
- 产生大量冗余数据增加成本
我们的解决方案:
javascript复制// 采样率控制
const shouldReport = Math.random() < 0.3
// 错误聚合上报
let errorQueue = []
const debouncedReport = _.debounce(() => {
sendToServer({ errors: errorQueue })
errorQueue = []
}, 5000)
window.addEventListener('error', (e) => {
errorQueue.push(e)
if(errorQueue.length > 5 || e.isCritical) {
debouncedReport.flush()
} else {
debouncedReport()
}
})
2.3 存储分析:数据怎么用?
建议分层存储:
- 原始数据存ClickHouse(低成本)
- 聚合指标存Elasticsearch(快速查询)
- 关键指标时序数据存Prometheus(告警)
我们用的分析SQL示例:
sql复制SELECT
page_url,
avg(lcp) as avg_lcp,
percentile(lcp, 0.9) as p90_lcp
FROM performance_metrics
WHERE date >= today() - 7
GROUP BY page_url
ORDER BY p90_lcp DESC
LIMIT 10
2.4 告警机制:何时该叫醒你?
配置原则:宁可漏报不要误报。我们分级设置:
- P0(立即电话):核心功能完全不可用
- P1(企业微信):关键指标异常波动>30%
- P2(邮件):非核心路径错误率>5%
3. 三天搭建实战方案
3.1 Day1:基础监控搭建
技术选型:
- 自建方案:Sentry(错误)+ Prometheus(性能)
- SaaS方案:Fundebug(错误)+ ARMS(性能)
我们最终选择Sentry+Prometheus组合,虽然要自己维护但数据更可控。用Docker快速部署:
bash复制# Sentry部署
docker run -d --name sentry-redis redis
docker run -d --name sentry-postgres -e POSTGRES_PASSWORD=secret postgres
docker run -d --name sentry -p 9000:9000 --link sentry-redis:redis --link sentry-postgres:postgres sentry
# Prometheus配置示例
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'frontend'
metrics_path: '/metrics'
static_configs:
- targets: ['your-app:3000']
3.2 Day2:关键指标埋点
必须埋点的五个位置:
- 页面入口:记录PV和基础环境信息
javascript复制trackPageView({
url: location.href,
ua: navigator.userAgent,
screen: `${window.screen.width}x${window.screen.height}`
})
- 路由切换:SPA应用需手动触发
javascript复制router.afterEach((to) => {
trackPageView(to.path)
})
- 接口请求:拦截axios/fetch
javascript复制axios.interceptors.response.use(response => {
trackApi({
url: response.config.url,
status: response.status,
duration: Date.now() - response.config.metadata.startTime
})
return response
})
- 按钮点击:关键操作追踪
html复制<button @click="trackClick('purchase_btn')">立即购买</button>
- 性能指标:使用web-vitals库
javascript复制import {getLCP} from 'web-vitals'
getLCP(console.log)
3.3 Day3:告警与可视化
Grafana看板配置要点:
- 首屏加载时间趋势图
- 接口错误率热力图
- JS错误类型饼图
- 地域分布拓扑图
告警规则示例(PromQL):
code复制# API成功率下降告警
sum(rate(http_requests_total{status=~"5.."}[5m])) by (endpoint)
/
sum(rate(http_requests_total[5m])) by (endpoint)
> 0.05
4. 避坑指南:我们踩过的雷
4.1 数据丢失问题
某次大促期间突然发现监控数据断崖式下跌,排查发现是SDK用了localStorage做缓存,但用户开启了Safari无痕模式。解决方案是降级到内存存储+指数退避重试。
4.2 采样失真案例
曾设置1%的采样率,结果漏掉了某个特定安卓机型的问题(该机型占比仅0.8%)。现在我们对以下情况全量采集:
- 任何错误请求
- LCP大于3s的慢加载
- 核心业务路径
4.3 隐私合规红线
在欧洲项目中被GDPR警告,因为采集了IP+设备ID。现在我们的方案:
- 敏感信息在前端脱敏
- 提供opt-out开关
- 存储加密处理
5. 高级技巧:让监控更有价值
5.1 错误智能归并
通过指纹算法将相同错误聚合:
javascript复制function generateErrorFingerprint(error) {
const stack = error.stack || ''
return md5(`${error.message}-${stack.split('\n')[0]}`)
}
5.2 性能瓶颈定位
使用Chrome DevTools的Performance面板录制后,通过脚本自动分析:
javascript复制const audits = await page.evaluate(() => {
return JSON.parse(JSON.stringify(window.performance.memory))
})
5.3 前后端链路追踪
在请求头注入traceId实现全链路追踪:
javascript复制axios.interceptors.request.use(config => {
config.headers['X-Trace-Id'] = generateTraceId()
return config
})
这套系统上线后,我们的线上问题平均修复时间从8小时缩短到47分钟。最惊喜的是有一次监控系统在凌晨3点捕获到支付接口的异常波动,自动触发降级方案,等早上大家上班时问题已经自动恢复,完美避过一次可能的大规模客诉。