前端日志监控系统备忘
一、前端错误的捕获
在 JavaScript 日志系统中,捕获错误的范围应该全面,涵盖前端应用中可能出现的各类问题,包括语法错误、运行时错误、资源加载错误以及用户行为异常等。以下是不同类型错误的捕获方法:
window.onerror: 可捕获常规错误(变量未定义、函数调用错误等)、计时器等错误,但不能捕获资源加载错误、promise的错误
window.addEventListener('error',fn,true):dom2级事件,跟上面一样,但是可以捕获到资源加载错误,需将事件监听器的 useCapture
参数设为 true
。
// 使用捕获阶段捕获资源加载错误 window.addEventListener('error', function(event) { console.log('Error loading resource:', event.target); }, true);
原因: 默认情况下,error
事件在事件传播的冒泡阶段触发,而浏览器的默认行为可能会使其无法触发。
unhandledrejection: 仅能捕获到Promise 的未处理拒绝(即没有使用.catch()
)、async代码中未显式抛出的错误(即没有throw,而是返回Promise.rect)
new Promise((_, reject) => reject(new Error('Promise Error'))); async function asyncError() { return Promise.reject(new Error('Async Error')); } asyncError(); // 无法通过 window.onerror 捕获
try catch: 用于捕获运行时的错误, 无法捕获语法错误(因为语法错误在js代码解析阶段就出错了);
try { console.log('start'); var a = ; // 语法错误 console.log('end'); } catch (err) { console.error('捕获错误:', err); }
performance.timing: 页面白屏或资源加载时间过长等问题
window.addEventListener('load', () => { const timing = performance.timing; const loadTime = timing.loadEventEnd - timing.navigationStart; if (loadTime > 3000) { console.warn('Page Load Slow:', loadTime); sendLog({ type: 'performance', loadTime }); } });
performance.memory: 内存泄露或占用过高(该api仅支持部分浏览器)
if (performance.memory) { setInterval(() => { const memoryUsage = performance.memory.usedJSHeapSize / 1024 / 1024; if (memoryUsage > 500) { console.warn('High Memory Usage:', memoryUsage); sendLog({ type: 'memory-warning', memoryUsage }); } }, 5000); }
二、日志系统的数据发送方案
方式 | 是否阻塞主线程 | 特点 |
---|---|---|
new Image().src | ❌ 不阻塞(相当于一个异步请求) |
简单快捷,但页面卸载时可能丢失请求 限制:发送的字节长度受get请求限制,不能用于复杂数据,更适合点击数量统计、页面访问次数统计等 |
navigator.sendBeacon() | ❌ 不阻塞 |
页面卸载、关闭时更可靠,后台传输。 限制:发送的数据量有限制(通常最多为64kb);没有任何反馈结果(例如promise或回调) |
XMLHttpRequest 或 fetch() | ❌ 不阻塞 |
更灵活,适合复杂数据,但页面关闭可能丢失 限制:如果在webworker中,只能使用fetch,不能使用xmlhttprequest |
备注:get请求限制
chrome、edge: 约2mb
Firefox: 65,535个字符
Safari: 80,000个字符
三、日志系统的数据发送优化点
1. 批量上报,避免频繁请求
使用队列数据结构存储数据,在达到一定数量或条件时批量发送。
存储数据的位置:IndexedDB(注意浏览器兼容性问题)
、 localStorage(容量有限制,大约5mb)、内存中(需要考虑页面卸载前把数据发送出去或者使用前两个存储对日志进行持久化)
备注:localstorage数据写满后会发生什么?
原数据不会被覆盖,而是直接抛出
QuotaExceededError
异常。
2. 数据压缩后发送
使用 JSON 压缩库(如 pako
)对数据进行压缩,然后服务端解压数据
import pako from 'pako'; function compressLogs(logData) { const compressed = pako.gzip(JSON.stringify(logData)); return compressed; } function sendLogsCompressed(logs) { const compressedLogs = compressLogs(logs); fetch('https://log-server.com/logs', { method: 'POST', headers: { 'Content-Encoding': 'gzip', 'Content-Type': 'application/json' }, body: compressedLogs }).catch(err => console.error('Failed to send logs:', err)); }
注意:应该通过 Web Worker 来压缩数据,以免阻塞主线程;Web Worker 运行在独立线程,与主线程隔离,无法访问以下内容:
window
对象- DOM API(例如
document
,alert
,querySelector
等) - 一些特定的 Web API,如
localStorage
、sessionStorage
不能直接操作页面 UI
可以访问:
self
(Worker 全局对象,相当于 worker 里的全局作用域)fetch
、XMLHttpRequest
(部分浏览器支持)postMessage
与主线程通信importScripts()
动态加载脚本
四、前端日志监控框架与平台
- Sentry:一个广泛使用的开源错误跟踪平台,支持 JavaScript 和其他前端框架的错误监控。
- LogRocket:一个前端日志记录和会话回放平台,除了错误监控,还能记录用户的交互和性能数据。
- Raygun:提供错误追踪、性能监控和用户行为分析的商业服务,支持前端和后端的日志管理