前端监控体系建设从异常捕获到性能分析大家好我是蔓蔓。在大厂工作时我负责搭建了团队的前端监控体系。一个好的监控系统能够帮助我们及时发现问题、定位问题、解决问题。今天我来和大家分享如何从零开始构建一套完整的前端监控体系。监控体系概述为什么需要监控及时发现问题在用户反馈之前发现并解决问题快速定位问题提供详细的错误信息和上下文优化用户体验通过性能监控发现瓶颈数据驱动决策基于真实数据进行优化监控分类监控类型监控内容工具推荐错误监控JS错误、资源加载失败、接口错误Sentry、Bugsnag性能监控页面加载、首屏时间、FPSLighthouse、Performance API用户行为点击、页面浏览、表单提交Google Analytics、Segment业务指标转化率、留存率、DAU自定义上报错误监控捕获JS错误// 全局错误捕获 window.addEventListener(error, (event) { const errorInfo { type: javascript_error, message: event.message, filename: event.filename, lineno: event.lineno, colno: event.colno, stack: event.error?.stack, timestamp: Date.now(), url: window.location.href, userAgent: navigator.userAgent }; // 上报错误 reportError(errorInfo); }); // Promise 未捕获异常 window.addEventListener(unhandledrejection, (event) { const errorInfo { type: promise_rejection, reason: event.reason?.message || String(event.reason), timestamp: Date.now(), url: window.location.href }; reportError(errorInfo); });捕获资源加载错误// 监听资源加载失败 document.addEventListener(error, (event) { const target event.target; if (target.tagName SCRIPT || target.tagName LINK || target.tagName IMG) { const errorInfo { type: resource_error, resourceType: target.tagName.toLowerCase(), src: target.src || target.href, timestamp: Date.now(), url: window.location.href }; reportError(errorInfo); } }, true);捕获接口错误// 重写 fetch const originalFetch window.fetch; window.fetch async function(...args) { const [url, options] args; const startTime Date.now(); try { const response await originalFetch(...args); // 检查 HTTP 状态码 if (!response.ok) { const errorInfo { type: api_error, url, method: options?.method || GET, status: response.status, duration: Date.now() - startTime, timestamp: Date.now() }; reportError(errorInfo); } return response; } catch (error) { const errorInfo { type: api_error, url, method: options?.method || GET, error: error.message, duration: Date.now() - startTime, timestamp: Date.now() }; reportError(errorInfo); throw error; } };性能监控使用 Performance API// 获取页面加载性能数据 function getPerformanceData() { const performance window.performance; const timing performance.timing; return { // DNS 查询时间 dnsTime: timing.domainLookupEnd - timing.domainLookupStart, // TCP 连接时间 tcpTime: timing.connectEnd - timing.connectStart, // 请求响应时间 requestTime: timing.responseEnd - timing.requestStart, // DOM 解析时间 domParseTime: timing.domComplete - timing.domInteractive, // 首字节时间 ttfb: timing.responseStart - timing.navigationStart, // 页面加载总时间 loadTime: timing.loadEventEnd - timing.navigationStart, // DOMContentLoaded 时间 domContentLoadedTime: timing.domContentLoadedEventEnd - timing.navigationStart }; } // 上报性能数据 window.addEventListener(load, () { const performanceData getPerformanceData(); reportPerformance(performanceData); });自定义性能指标// 自定义计时 function startTimer(name) { performance.mark(${name}_start); } function endTimer(name) { performance.mark(${name}_end); performance.measure(name, ${name}_start, ${name}_end); const measure performance.getEntriesByName(name)[0]; if (measure) { reportPerformance({ name, duration: measure.duration, timestamp: Date.now() }); // 清理标记 performance.clearMarks(${name}_start); performance.clearMarks(${name}_end); performance.clearMeasures(name); } } // 使用示例 startTimer(api_request); await fetch(/api/data); endTimer(api_request);FPS 监控// FPS 监控 class FPSMonitor { constructor() { this.frameCount 0; this.lastTime performance.now(); this.fps 60; } start() { this.animationId requestAnimationFrame(() this.tick()); } stop() { if (this.animationId) { cancelAnimationFrame(this.animationId); } } tick() { this.frameCount; const currentTime performance.now(); if (currentTime - this.lastTime 1000) { this.fps Math.round(this.frameCount * 1000 / (currentTime - this.lastTime)); this.frameCount 0; this.lastTime currentTime; // 上报 FPS reportPerformance({ name: fps, value: this.fps, timestamp: Date.now() }); } this.animationId requestAnimationFrame(() this.tick()); } } // 使用示例 const fpsMonitor new FPSMonitor(); fpsMonitor.start();用户行为监控点击事件追踪// 自动追踪点击事件 document.addEventListener(click, (event) { const target event.target; // 只追踪有>// 页面浏览追踪 class PageTracker { constructor() { this.pageStartTime Date.now(); this.currentPage window.location.pathname; } trackPageView() { const pageData { type: page_view, page: window.location.pathname, referrer: document.referrer, timestamp: Date.now() }; reportBehavior(pageData); } trackPageLeave() { const pageData { type: page_leave, page: this.currentPage, duration: Date.now() - this.pageStartTime, timestamp: Date.now() }; reportBehavior(pageData); } } // 使用示例 const pageTracker new PageTracker(); pageTracker.trackPageView(); // 页面离开时上报 window.addEventListener(beforeunload, () { pageTracker.trackPageLeave(); });数据上报上报策略class Reporter { constructor() { this.queue []; this.batchSize 10; this.maxQueueSize 100; this.flushInterval 5000; // 定时上报 setInterval(() this.flush(), this.flushInterval); // 页面卸载时上报 window.addEventListener(beforeunload, () this.flush(true)); } add(data) { if (this.queue.length this.maxQueueSize) { // 超出队列大小丢弃最早的数据 this.queue.shift(); } this.queue.push({ ...data, timestamp: Date.now(), sessionId: this.getSessionId() }); // 达到批量大小立即上报 if (this.queue.length this.batchSize) { this.flush(); } } async flush(force false) { if (this.queue.length 0) return; const data [...this.queue]; this.queue []; try { await fetch(/api/report, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(data), // 确保页面卸载时也能发送 keepalive: force }); } catch (error) { // 上报失败放回队列 this.queue [...data, ...this.queue]; } } getSessionId() { let sessionId localStorage.getItem(sessionId); if (!sessionId) { sessionId ${Date.now()}-${Math.random().toString(36).substr(2, 9)}; localStorage.setItem(sessionId, sessionId); } return sessionId; } } // 全局实例 export const reporter new Reporter();数据可视化仪表盘设计// 简化版仪表盘组件 class Dashboard { constructor(container) { this.container container; this.data {}; } update(data) { this.data { ...this.data, ...data }; this.render(); } render() { this.container.innerHTML div classdashboard div classstat-card div classstat-value${this.data.errorCount || 0}/div div classstat-label今日错误数/div /div div classstat-card div classstat-value${this.data.avgLoadTime || 0}ms/div div classstat-label平均加载时间/div /div div classstat-card div classstat-value${this.data.avgFps || 60}/div div classstat-label平均 FPS/div /div div classstat-card div classstat-value${this.data.dau || 0}/div div classstat-label日活用户/div /div /div ; } }告警机制告警规则配置const alertRules [ { id: error_rate, name: 错误率告警, condition: (data) data.errorRate 1, threshold: 1, level: critical, message: 错误率超过 1% }, { id: slow_load, name: 加载缓慢告警, condition: (data) data.avgLoadTime 3000, threshold: 3000, level: warning, message: 平均加载时间超过 3 秒 }, { id: fps_drop, name: FPS 下降告警, condition: (data) data.avgFps 30, threshold: 30, level: critical, message: 平均 FPS 低于 30 } ]; // 检查告警 function checkAlerts(data) { const triggeredAlerts []; for (const rule of alertRules) { if (rule.condition(data)) { triggeredAlerts.push({ ...rule, timestamp: Date.now(), currentValue: getCurrentValue(data, rule) }); } } return triggeredAlerts; }总结一个完整的前端监控体系应该包含错误监控捕获 JS 错误、资源错误、接口错误性能监控页面加载时间、FPS、自定义指标用户行为点击事件、页面浏览、表单提交数据上报批量上报、离线缓存、重试机制数据可视化仪表盘、趋势图、实时监控告警机制多级别告警、通知渠道技术应当有温度一个好的监控系统不仅能帮助开发者发现问题更能帮助产品团队做出数据驱动的决策最终提升用户体验。你在前端监控方面有什么经验欢迎在评论区交流