前端错误上报
错误上报 监控 前端
对于前端来说,往往是用户反馈了才知道已经错了。
为了让前端也能和后端一样,需要将线上的 JavaScript 代码监控起来,当用户端浏览器出现异前端第一时间被通知到。
采集哪些数据
主要原则就是避开用户敏感字段,采集浏览器版本、操作系统版本、报错的 msg 信息等。
如何采集错误
前端错误大体上可以分成两类:
-
代码执行的错误
-
资源加载的错误
1.代码执行错误
try…catch
try-catch 是最弱的, 缺点是:
- 使用 try…catch 包裹,影响代码可读性
- 无法处理语法错误
try { var error = 'error'; // 中文输入法的分号 } catch(e) { console.log('我感知不到错误'); console.log(e); } - 无法处理异步中的错误
try { setTimeout(() => { error; // 异步错误 }); } catch (e) { console.log("不会执行"); console.log(e); }
window.onerror
window.onerror 要强一点。无论是异步还是非异步错误,onerror 都能捕获到运行时错误。
/**
* @param {String} errorMessage 错误信息
* @param {String} scriptURI 出错的文件
* @param {Long} lineNumber 出错代码的行号
* @param {Long} columnNumber 出错代码的列号
* @param {Object} errorObj 错误的详细信息,Anything
*/
window.onerror = function (
errorMessage,
scriptURI,
lineNumber,
columnNumber,
errorObj
) {
// code..
};
window.onerror 的缺点:
- 监听不到资源加载的报错
- onerror 事件处理函数只能声明一次,不会重复执行多个回调
window.addEventListener(‘error’)
window.addEventListener 可以监听到资源加载报错,也可以注册多个事件处理函数。
var fn = (window.onerror = function () {
// 只能监听到js执行的错误,无法监听资源加载的错误
console.log(arguments);
});
window.addEventListener("error", fn); // 可以监听到js执行的错误,和资源加载的错误
window.addEventListener("error", fn);
为捕获状态时(第三个参数为 true)能捕获到 js 执行错误,也能捕获带有 src 的标签元素的加载错误。
为冒泡状态时(第三个参数为 false)能捕获到 js 执行错误,不能捕获带有 src 的标签元素的加载错误。
window.onerror 对比 window.addEventListener(‘error’)
- onerror 只能声明一次,而事件处理器则可以绑定多个回调函数。
- onerror 没有办法监听到资源加载的报错,而 addEventListener 则可以监听到。
promise 中的异常
promise 的报错比较娇贵,try…catch, window.onerror, widow.addEventlistener 都无法监听到 promise 的报错。 promise 中的报错顺序是:
- 如果有 catch 等捕获函数,则走 catch 捕获函数。catch 捕获函数如果没有抛出新的异常,则下一个 then 将会认为没有什么报错,会继续执行。
- 如果没有 catch 等捕获函数,可以注册 window.addEventListener(‘unhandledrejection’) 来处理。
微信小程序 app.onError
App({
onError(msg) {
this.monitor.onError(msg);
},
});
微信小程序代码中没有办法获取到 window 对象,自然不能用 window.onError, 小程序官方提供了 app.onError 的方法。
sourceMap
生产环境的代码是 webpack 混淆打包之后,不好定位。
解决办法是开启 webpack 的 source-map,我们利用 webpack 打包后的生成的一份.map 的脚本文件就可以让浏览器对错误位置进行追踪了。
var path = require("path");
module.exports = {
devtool: "source-map",
mode: "development",
entry: "./client/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "client"),
},
};
资源加载失败
捕获资源加载失败的方法,主要如下:
- imgObj.onerror()
- performance.getEntries() 获取到成功加载的资源,对比可以间接的捕获错误
varallImgs = document.getElementsByTagName("image"); varloadedImgs = performance .getEntries() .filter((i) => i.initiatorType === "img"); //最后 allIms和 loadedImgs对比即可找出图片资源未加载项目 - window.addEventListener(‘error’, fn, true) 会捕获但是不冒泡,所以 window.onerror 不会触发,捕获阶段可以触发
如何上报错误
- 采用 ajax 上报
- 使用 image 上报
一般来说,采用利用 image 对象的方式上报错误更好;
使用图片发送 get 请求,上报信息,由于浏览器对图片有缓存,同样的请求,图片只会发送一次,避免重复上报。
var entry = {};
function report(url, data) {
if (!url || !data) {
return;
}
var image = document.createElement("img");
var items = [];
for (var key in data) {
if (data[key]) {
items.push(key + "=" + encodeURIComponent(data[key]));
}
}
var name = "img_" + +new Date();
entry[name] = image;
image.onload = image.onerror = function () {
console.log(arguments);
entry[name] = image = image.onload = image.onerror = null;
delete entry[name];
};
image.src = url + (url.indexOf("?") < 0 ? "?" : "&") + items.join("&");
}
错误监控开源方案:
- sentry