前端日志监控系统备忘
一、前端错误的捕获
在 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: 开发人员主动捕获特定业务场景中的错误,async内部代码块错误
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); }
二、日志系统的传输
1. new image().src = xxx : 发送简单的数据,例如点击次数等
2.navigator.sendBeacon()
:专门用于发送小量日志数据的 API,适合页面卸载时发送数据,且不会阻塞页面关闭。
优点
性能优势:
sendBeacon
不会阻塞页面卸载过程,确保数据能够在页面跳转或关闭时被发送,而不影响页面的加载性能。可靠性:即使页面正在卸载,
sendBeacon
也会尽量确保数据发送到服务器,因此它适用于发送需要持久化的数据(如日志、分析数据等)。限制
由于它的设计是为了发送小量数据,
sendBeacon
发送的数据量相对较小(通常约为 64 KB);无响应回调::与
XMLHttpRequest
或fetch
不同,sendBeacon
不返回一个 Promise 或回调,服务器的响应对用户而言没有任何影响。
3.XMLHttpRequest
或 fetch
:用于将日志数据发送到服务器。对于大多数日志数据传输,fetch
更为常用,因为它基于 Promise,更灵活且更现代。
三、日志系统的优化
1. 数据分批发送
将日志数据缓存到本地,在达到一定数量或条件时再发送。
实现方法1:
- 使用内存中的队列存储日志数据。
- 当队列长度达到阈值(如 10 条)或时间间隔到达(如 1 分钟)时批量发送。
const logQueue = []; const MAX_LOGS = 10; const FLUSH_INTERVAL = 60000; // 1 minute function addLog(log) { logQueue.push(log); if (logQueue.length >= MAX_LOGS) { sendLogs(); } } function sendLogs() { if (logQueue.length === 0) return; fetch('https://log-server.com/logs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(logQueue) }).then(() => { logQueue.length = 0; // Clear queue }).catch(err => console.error('Failed to send logs:', err)); } // Periodic flush setInterval(sendLogs, FLUSH_INTERVAL);
实现方法2:
- 利用
IndexedDB
或localStorage
缓存数据。 - 监听
navigator.onLine
状态。在网络断开时暂存日志,网络恢复后再批量发送
function saveLogToIndexedDB(log) { const request = indexedDB.open('LogDB', 1); request.onsuccess = function(event) { const db = event.target.result; const transaction = db.transaction(['logs'], 'readwrite'); const store = transaction.objectStore('logs'); store.add(log); }; request.onupgradeneeded = function(event) { const db = event.target.result; db.createObjectStore('logs', { autoIncrement: true }); }; } window.addEventListener('online', sendLogs); function sendLogs() { if (navigator.onLine) { // Read from localStorage and send } }
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)); }
3. 利用web worker避免阻塞主线程
通过 Web Worker 在后台处理日志收集和发送,避免阻塞主线程,主线程通过 postMessage
向 Worker 发送日志。
注意: Web Worker 运行在不同的线程中,因此它无法直接访问
window
对象及其相关的 Web API(如sessionStorage
、localStorage
、XMLHttpRequest、navigator.sendBeacon等
),只能使用fetch请求。
四、前端日志监控框架与平台
- Sentry:一个广泛使用的开源错误跟踪平台,支持 JavaScript 和其他前端框架的错误监控。
- LogRocket:一个前端日志记录和会话回放平台,除了错误监控,还能记录用户的交互和性能数据。
- Raygun:提供错误追踪、性能监控和用户行为分析的商业服务,支持前端和后端的日志管理