UNPKG

web-see-monitor

Version:

前端监控 SDK,包含性能监控、错误监控和用户行为监控

401 lines (328 loc) 8.98 kB
# Web Monitor SDK 一个轻量级的前端监控 SDK,用于收集网页性能指标、错误信息和用户行为数据。 ## 特性 - 🚀 轻量级,零依赖 - 📊 性能监控 - 首屏加载时间 - FCP (First Contentful Paint) - LCP (Largest Contentful Paint) - FID (First Input Delay) - CLS (Cumulative Layout Shift) - 🐛 错误监控 - JS 运行时错误 - Promise 未捕获异常 - 资源加载错误 - HTTP 请求异常 - 👨‍💻 用户行为跟踪 - PV/UV 统计 - 用户点击行为 - 页面停留时间 - 路由变化 ## 安装 ```bash npm install web-monitor-sdk ``` ## 核心功能 ### 1. 性能监控 - **页面加载性能** - 首屏加载时间(FP) - 首次内容绘制(FCP) - 最大内容绘制(LCP) - 首次输入延迟(FID) - 累积布局偏移(CLS) - **资源加载性能** - 资源加载时间统计 - 资源加载瀑布图 - 静态资源缓存命中率 - **接口性能** - API 请求耗时统计 - 接口成功/失败率 - 慢请求追踪 ### 2. 错误监控 - **JS 错误** - 运行时错误捕获 - Promise 异常监控 - 跨域脚本错误处理 - **资源错误** - 图片/脚本加载失败 - HTTP 404/500 等状态码监控 - 接口超时监控 - **控制台错误** - console.error 监控 - 自定义错误上报 ### 3. 用户行为分析 - **页面访问** - PV/UV 统计 - 访问来源分析 - 停留时长统计 - **用户交互** - 点击行为追踪 - 滚动深度分析 - 表单操作记录 ## 优化措施 ### 1. 数据上报优化 - **批量上报** - 采用队列缓存,批量上报减少请求次数 - 支持配置上报阈值和时间间隔 - 页面卸载前确保数据发送完成 - **数据压缩** - 采用 JSON 压缩算法 - 大数据量采用分片上传 - 支持 gzip 压缩 ### 2. 性能优化 - **采样控制** - 支持按比例采样上报 - 重要用户/错误数据优先上报 - 动态调整采样率 - **节流与防抖** - 高频事件节流处理 - 连续错误防抖优化 - 避免重复上报 ### 3. 稳定性优化 - **容错处理** - SDK 异常自动恢复 - 关键流程容错 - 上报失败重试机制 - **浏览器兼容** - 优雅降级处理 - Polyfill 按需加载 - 主流浏览器全覆盖 ## 实现细节 ### 1. 性能数据采集 - 使用 Performance API 采集加载性能 - 使用 PerformanceObserver 监听性能事件 - 通过 requestAnimationFrame 计算渲染性能 ### 2. 错误监控实现 - window.onerror 捕获 JS 运行时错误 - unhandledrejection 事件捕获 Promise 异常 - 重写 XMLHttpRequest 和 fetch 监控接口异常 ### 3. 行为数据收集 - 使用事件委托优化点击监听 - 通过 MutationObserver 监控 DOM 变化 - 路由监听支持 hash 和 history 模式 ### 4. 上报优化实现 - 使用 requestIdleCallback 在空闲时段上报 - 采用 Navigator.sendBeacon 处理页面卸载场景 - IndexedDB 存储离线数据 // ... 前面内容保持不变 ... ## 优化措施及实现 ### 1. 数据上报优化 #### 批量上报实现 ```typescript class Reporter { private queue: any[] = []; private timer: number | null = null; private readonly threshold = 10; // 队列阈值 private readonly interval = 5000; // 上报间隔 push(data: any) { this.queue.push(data); if (this.queue.length >= this.threshold) { this.flush(); } else { this.setupTimer(); } } private setupTimer() { if (!this.timer) { this.timer = window.setTimeout(() => this.flush(), this.interval); } } private flush() { if (this.queue.length === 0) return; // 使用 sendBeacon 保证页面卸载时数据发送 const data = JSON.stringify(this.queue); navigator.sendBeacon('/collect', data); this.queue = []; if (this.timer) { clearTimeout(this.timer); this.timer = null; } } } ``` #### 数据压缩实现 ```typescript class DataCompressor { compress(data: any) { // 1. 移除冗余字段 const cleaned = this.removeRedundant(data); // 2. 数据编码压缩 const compressed = this.encode(cleaned); // 3. 大数据分片处理 return this.slice(compressed); } private encode(data: any) { // 短字符串映射 const dictionary: Record<string, string> = { 'error': 'e', 'warning': 'w', 'info': 'i', 'performance': 'p' }; return JSON.stringify(data).replace( /("error"|"warning"|"info"|"performance")/g, match => dictionary[match.replace(/"/g, '')] || match ); } } ``` ### 2. 性能优化实现 #### 采样控制实现 ```typescript class SamplingController { private readonly defaultRate = 0.1; // 默认采样率10% shouldReport(data: any): boolean { // 错误数据100%采样 if (data.type === 'error') return true; // VIP用户100%采样 if (data.userType === 'vip') return true; // 其他数据按比例采样 return Math.random() < this.getRate(data.type); } private getRate(type: string): number { const rateMap = { performance: 0.1, behavior: 0.05, custom: 0.01 }; return rateMap[type] || this.defaultRate; } } ``` #### 节流防抖实现 ```typescript function debounce<T extends (...args: any[]) => any>( fn: T, wait: number ): (...args: Parameters<T>) => void { let timer: number | null = null; return function(...args: Parameters<T>) { if (timer) clearTimeout(timer); timer = window.setTimeout(() => { fn.apply(this, args); timer = null; }, wait); }; } // 错误上报防抖 const reportError = debounce((error: Error) => { reporter.send({ type: 'error', message: error.message, stack: error.stack }); }, 2000); ``` ### 3. 稳定性优化实现 #### 容错处理实现 ```typescript class ErrorBoundary { private retryTimes = 3; async sendData(data: any) { for (let i = 0; i < this.retryTimes; i++) { try { await this.send(data); return; } catch (err) { if (i === this.retryTimes - 1) { // 存储到 IndexedDB 待后续上报 await this.saveToIndexedDB(data); } // 指数退避重试 await this.sleep(Math.pow(2, i) * 1000); } } } private async saveToIndexedDB(data: any) { const db = await this.openDB(); const tx = db.transaction('reports', 'readwrite'); await tx.objectStore('reports').add(data); } } ``` #### 浏览器兼容实现 ```typescript class BrowserAdapter { // Performance API 兼容 getFirstPaint() { if (window.performance && window.performance.getEntriesByType) { const [entry] = performance.getEntriesByType('paint'); return entry ? entry.startTime : null; } // 降级方案 return this.getFallbackTiming(); } // 优雅降级 private getFallbackTiming() { if (performance.timing) { return performance.timing.domLoading - performance.timing.navigationStart; } return Date.now(); // 最后的降级方案 } } ``` ### 4. 离线存储实现 ```typescript class OfflineStorage { private readonly DB_NAME = 'monitor_sdk'; private readonly STORE_NAME = 'offline_data'; async init() { return new Promise((resolve, reject) => { const request = indexedDB.open(this.DB_NAME, 1); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(request.result); request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; db.createObjectStore(this.STORE_NAME, { keyPath: 'id', autoIncrement: true }); }; }); } async saveOfflineData(data: any) { const db = await this.init(); return new Promise((resolve, reject) => { const tx = (db as IDBDatabase) .transaction(this.STORE_NAME, 'readwrite'); const store = tx.objectStore(this.STORE_NAME); const request = store.add({ data, timestamp: Date.now() }); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } } ``` ## 使用示例 ```typescript const monitor = new WebMonitor({ appId: 'your-app-id', // 开启性能监控 enablePerformance: true, // 开启错误监控 enableError: true, // 采样率配置 sampling: { performance: 0.1, behavior: 0.05 }, // 上报配置 report: { threshold: 10, interval: 5000 } }); // 启动监控 monitor.start(); ``` 这些优化措施的实现代码展示了: 1. 如何实现批量上报和数据压缩 2. 采样控制的具体逻辑 3. 防抖节流的实现方式 4. 错误重试和容错处理 5. 浏览器兼容性处理 6. 离线数据存储的实现 通过这些实现,可以确保监控 SDK 在各种场景下都能稳定高效地运行。