前端日志记录与上报是指利用JavaScript在浏览器环境中收集运行时产生的错误信息、性能数据与用户行为轨迹,并通过网络请求发送到服务端存储分析的过程。它让开发者能还原线上真实故障场景,而不再依赖用户口头描述。
一、为什么要做前端日志监控
传统开发模式下,前端代码部署后就像断了线的风筝。用户遇到白屏、点击无反应,开发侧往往一无所知。等到客服反馈,用户也记不清操作步骤,排查只能靠猜。前端日志监控把浏览器内的异常主动记录下来,相当于给每个用户装了黑匣子。
除了捕获错误,日志还能反映性能瓶颈。比如某个组件渲染超过三秒,或者某个接口失败率突增,这些都可以通过定时采集与上报提前发现。对于缺乏专业运维体系的小团队,一套轻量的JS日志方案就能覆盖大部分线上可观测性需求。
二、基础错误捕获方式
JavaScript提供了全局异常监听接口,最基础的是window.onerror。它可以捕获未被try-catch处理的同步错误,以及部分异步错误。函数签名中包含错误信息、文件地址、行号与列号,足以定位问题代码。
不过window.onerror对资源加载错误(如图片、脚本404)无能为力,这类错误不会冒泡到window。需要额外监听error事件的捕获阶段,并判断事件目标是否为HTML元素。下面代码展示了组合使用的写法。
// 监听全局JS运行时错误
window.onerror = function(message, source, lineno, colno, error) {
const log = {
type: 'js_error',
message: message,
source: source,
lineno: lineno,
colno: colno,
stack: error && error.stack
};
sendLog(log);
};
// 捕获资源加载错误,使用捕获阶段
window.addEventListener('error', function(e) {
if (e.target && (e.target.tagName === 'IMG' || e.target.tagName === 'SCRIPT')) {
const resLog = {
type: 'resource_error',
url: e.target.src || e.target.href
};
sendLog(resLog);
}
}, true);
function sendLog(data) {
// 此处可替换为真实上报地址
const url = 'https://log.ipipp.com/collect';
const body = JSON.stringify(data);
if (navigator.sendBeacon) {
navigator.sendBeacon(url, body);
} else {
fetch(url, { method: 'POST', body: body, keepalive: true });
}
}
三、Promise与接口异常监控
现代前端大量使用Promise,未处理的rejection不会触发window.onerror。必须通过unhandledrejection事件采集。同时,业务接口返回非200或约定错误码,也属于需要上报的逻辑异常。
我们可以在封装的请求层中统一拦截响应,将失败请求的方法、地址与状态码记录下来。这样既避免散落在业务代码里,也保证日志格式一致。以下示例演示了Promise异常与fetch封装的结合。
// 捕获未处理的Promise拒绝
window.addEventListener('unhandledrejection', function(e) {
const reason = e.reason;
sendLog({
type: 'promise_error',
message: reason && reason.message,
stack: reason && reason.stack
});
});
// 统一请求上报
async function request(url, options) {
try {
const res = await fetch(url, options);
if (!res.ok) {
sendLog({
type: 'http_error',
url: url,
status: res.status
});
}
return res;
} catch (err) {
sendLog({
type: 'network_error',
url: url,
message: err.message
});
throw err;
}
}
四、上报策略与数据防丢
日志上报若每次错误都发请求,容易淹没正常业务流量。通常采用采样上报,比如百分之十的用户全量采集,其余用户只报致命错误。另外,页面卸载时普通请求会被取消,应使用navigator.sendBeacon保证送达。
为防止本地堆积,可结合localStorage做失败重试。当网络异常时把日志暂存,下次启动时补发。下面的表格对比了三种上报方式的差异。
| 方式 | 可靠性 | 适用场景 |
|---|---|---|
| fetch | 中 | 页面活跃时常规上报 |
| sendBeacon | 高 | 页面关闭前兜底发送 |
| localStorage+重试 | 高 | 弱网或离线暂存 |
五、日志内容设计与隐私
一条有用的日志至少包含发生时间、错误类型、页面路径与用户标识。注意不要收集手机号、身份证等敏感信息,避免合规风险。可以通过白名单控制上报字段。
建议在每条日志中加入page_url与user_id的脱敏值,方便聚合分析又不过度追踪。服务端接收后按类型建索引,就能在灰度发布后快速观察错误率波动。
前端监控不是装好就一劳永逸,需要定期审视上报量、错误分类是否准确,才能持续发挥作用。
六、完整轻量封装示例
把前面能力合并成一个类,在入口文件初始化即可。以下代码可直接放入项目,修改上报地址就能跑起来。
class FrontLog {
constructor(opt) {
this.url = opt.url || 'https://log.ipipp.com/collect';
this.sample = opt.sample || 1;
this.bind();
}
bind() {
window.onerror = (msg, src, line, col, err) => {
this.report({ type: 'js_error', msg, src, line, col, stack: err && err.stack });
};
window.addEventListener('error', (e) => {
if (e.target && e.target.tagName) {
this.report({ type: 'res_error', url: e.target.src || e.target.href });
}
}, true);
window.addEventListener('unhandledrejection', (e) => {
this.report({ type: 'promise_error', msg: e.reason && e.reason.message });
});
}
report(data) {
if (Math.random() > this.sample) return;
data.t = Date.now();
const body = JSON.stringify(data);
if (navigator.sendBeacon) {
navigator.sendBeacon(this.url, body);
} else {
fetch(this.url, { method: 'POST', body, keepalive: true });
}
}
}
new FrontLog({ url: 'https://log.ipipp.com/collect', sample: 0.1 });
JavaScript前端日志错误监控修改时间:2026-08-04 13:06:41