前端捕获未处理JS错误需同时监听window.onerror、addEventListener('error')和unhandledrejection事件,用Beacon API优先上报,辅以采样去重和上下文快照。

前端日志怎么捕获未处理的 JS 错误?
直接监听 window.onerror 和 window.addEventListener('error') 是最基础也最可靠的入口,但二者覆盖范围不同:window.onerror 捕获全局同步错误(如语法错误、运行时异常),而 addEventListener('error') 主要捕获资源加载失败(、 等)。
必须同时注册两者,否则会漏掉大量错误。尤其注意:Promise 拒绝(unhandledrejection)需单独监听,它不会触发前两者。
-
window.onerror回调参数顺序固定:(message, source, lineno, colno, error),其中error是原生Error实例,可取error.stack -
unhandledrejection事件的event.reason可能是字符串或Error,需统一转为对象再提取堆栈 - 不要在这些监听器里执行复杂逻辑或发起多个请求,避免阻塞或引发新错误
如何把错误数据可靠地发到服务器?
用 fetch 发送日志看似简单,但默认不带凭证、可能被 CORS 拦截、失败后无重试——这会导致大量错误“静默丢失”。关键不是“发出去”,而是“确保发成功”。
- 优先用
Beacon API(navigator.sendBeacon()):页面卸载时仍能异步发出,不阻塞关闭,且自动携带 cookie(如果后端允许) - 若不支持 Beacon(如旧版 Safari),降级用
fetch(..., { keepalive: true }),但需确认浏览器兼容性 - 绝对避免在
beforeunload中用普通fetch或XMLHttpRequest:多数浏览器会中止请求 - 上报 payload 应精简:只传
message、stack、url、userAgent、timestamp和必要业务上下文(如当前路由、用户 ID)
如何避免日志上报干扰正常业务或触发限流?
前端错误常呈爆发式(如 CDN 失效导致全站脚本报错),不做节流会压垮后端接口,甚至拖慢页面。必须在客户端做采样和缓冲。
立即学习“Java免费学习笔记(深入)”;
- 对同一错误(基于
message + stack哈希)做 5 分钟内去重,避免重复上报 - 启用随机采样(如
Math.random() )控制整体上报率,线上环境建议 ≤ 10% - 错误积压时用内存队列暂存(如最多缓存 20 条),配合定时器批量发送,减少请求数
- 上报接口必须返回 HTTP 2xx 才算成功;非 2xx 响应(包括网络失败)应计入失败计数,并尝试降级(如改用
Imageping 方式打点)
const logQueue = [];
let isSending = false;
function enqueueLog(log) {
if (logQueue.length >= 20) return;
logQueue.push(log);
if (!isSending) sendBatch();
}
function sendBatch() {
if (logQueue.length === 0) return;
isSending = true;
const payload = { logs: logQueue.splice(0, 10) };
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/log', JSON.stringify(payload));
isSending = false;
} else {
fetch('/api/log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
keepalive: true
}).finally(() => {
isSending = false;
if (logQueue.length > 0) setTimeout(sendBatch, 100);
});
}
}
上报链路里最容易被忽略的是「错误上下文丢失」:比如用户在表单提交后报错,但没记录当时输入了什么。这类信息不能全量上报(涉及隐私),但可设计白名单字段(如表单项 name 属性)做脱敏快照——这个动作得在错误发生前就埋好钩子,而不是等 onerror 触发才去抓。











