一、系统架构设计
异常监控系统需满足三大核心需求:实时性、可靠性和可观测性。系统采用分层架构设计:
- 数据采集层:捕获JS错误、资源加载失败、异步请求异常等
- 数据处理层:实现错误去重、数据缓存、录屏关联
- 数据上报层:支持多种上报策略,确保数据不丢失
- 可视化层:提供录屏回放和错误分析界面
核心数据结构示例:
interface ErrorData {id: string;type: 'js' | 'resource' | 'async';message: string;stack?: string;timestamp: number;fingerprint: string; // 错误唯一标识sessionId: string; // 会话IDscreenId?: string; // 关联录屏ID}class MonitorSystem {private errorQueue: ErrorData[] = [];private errorCache = new Set<string>();private screenRecorder: any; // 录屏实例private timer?: NodeJS.Timeout;private readonly MAX_QUEUE_SIZE = 100;private readonly REPORT_INTERVAL = 60000;}
二、全局错误监听实现
1. 错误类型分类处理
- JS错误:通过
window.addEventListener('error')捕获 - 资源错误:监听
window.addEventListener('error')的target.src属性 - 异步错误:重写
Promise.prototype.then/catch和XMLHttpRequest.send
// 资源加载错误处理示例window.addEventListener('error', (event) => {if (event.target && (event.target as HTMLScriptElement).src) {const errorData = {type: 'resource',message: `Resource load failed: ${(event.target as HTMLScriptElement).src}`,timestamp: Date.now()};this.addErrorToQueue(errorData);}}, true); // 使用捕获阶段
2. 主动上报接口设计
提供灵活的上报接口,支持自定义错误数据:
class MonitorSystem {public reportError(error: Partial<ErrorData>) {const fullError = {id: uuid(),type: 'custom',timestamp: Date.now(),...error};this.addErrorToQueue(fullError);}}
三、数据持久化与恢复机制
1. 本地存储方案
采用三级存储策略:
- 内存队列:实时处理最新错误
- LocalStorage:持久化未上报数据
- IndexedDB:存储录屏数据(当数据量>5MB时)
private loadFromStorage() {const savedErrors = localStorage.getItem('errorQueue');if (savedErrors) {const parsed = JSON.parse(savedErrors);this.errorQueue = parsed.filter(err => {const isValid = this.validateError(err);if (!isValid) this.errorCache.delete(err.fingerprint);return isValid;});}}
2. 智能合并策略
系统初始化时执行数据恢复流程:
- 从存储加载历史错误
- 检查错误指纹是否已存在
- 合并相同会话的连续错误
- 优先保留最新错误数据
四、错误去重与关联分析
1. 指纹生成算法
采用多重特征组合生成唯一标识:
private generateFingerprint(error: ErrorData) {const { type, message, stack, sessionId } = error;return crypto.createHash('sha256').update(`${type}|${message}|${stack?.substring(0, 200)}|${sessionId}`).digest('hex');}
2. 录屏关联机制
通过rrweb实现行为录制:
- 错误发生时记录时间戳
- 在录屏数据中标记错误点
- 上报时关联最近的录屏片段
private initScreenRecording() {this.screenRecorder = new rrweb.record({emit: (event) => {this.screenEvents.push(event);// 自动清理旧数据if (this.screenEvents.length > 1000) {this.screenEvents.shift();}}});}
五、智能上报策略
1. 三种上报方式对比
| 方式 | 适用场景 | 优缺点 |
|---|---|---|
| Request | 大数据量/需要响应 | 占用资源较多 |
| Image | 跨域环境 | 简单可靠,无法获取响应 |
| Navigator | 移动端/低性能设备 | 兼容性好,功能有限 |
2. 定时上报实现
private startReportTimer() {if (this.timer) clearInterval(this.timer);this.timer = setInterval(() => {if (this.errorQueue.length === 0) return;const batch = this.errorQueue.splice(0, 20); // 批量上报this.sendBatch(batch).catch(err => {// 上报失败时恢复队列this.errorQueue.unshift(...batch);console.error('Report failed:', err);});}, this.REPORT_INTERVAL);}
六、生产环境优化建议
- 采样率控制:根据QPS动态调整监控比例
- 错误分级:区分致命错误和普通错误
- 隐私保护:敏感数据脱敏处理
- 性能监控:集成页面性能指标采集
- 告警集成:对接监控告警系统
七、扩展功能实现
1. 录屏回放组件
<div id="player-container"></div><script>function playbackError(screenId) {const events = getScreenEvents(screenId); // 从存储获取const player = new rrwebPlayer({target: document.getElementById('player-container'),data: { events }});player.play(findErrorTime(screenId)); // 定位到错误发生时刻}</script>
2. SPA路由监控
private initPageHandler() {// 监听路由变化if (window.history.pushState) {const originalPush = history.pushState;history.pushState = (...args) => {originalPush.apply(history, args);this.handleRouteChange();};}}private handleRouteChange() {// 结束当前会话,创建新会话this.flushErrorQueue();this.sessionId = uuid();}
总结
构建异常监控系统需要综合考虑错误捕获、数据持久化、智能上报等多个环节。通过分层架构设计和模块化实现,可以创建出既稳定又灵活的监控解决方案。实际开发中建议:
- 先实现核心功能,再逐步扩展
- 重视数据安全和隐私保护
- 建立完善的测试用例覆盖各种错误场景
- 监控系统本身需要具备高可用性设计
完整实现代码可参考开源社区的异常监控项目,结合自身业务需求进行定制化开发。对于中大型项目,建议将监控系统与日志服务、对象存储等云服务集成,构建完整的可观测性体系。