乐趣区

前端错误监控以及上报方法总结

捕获错误的常见的几种方法
Global​Event​Handlers​.onerror
windows 下面的全局 error 事件程序,当有 javaSript 脚本运行错误或者资源 <img>、<script> 加载失败时,都会触发 Event 接口的 error 事件,也能被 window.addEventListener 捕获到
两种写法
window.onerror = function(message, source, lineno, colno, error) {
console.log(message)// 字符串错误信息
console.log(source)// 发生错误的脚本
console.log(lineno)// 发生错误的行号
console.log(colno)// 发生错误的列号
console.log(error)//Erroe 对象

return true// 将代码错误定格在捕获阶段
}

window.addEventListener(‘error’, (msg, url, row, col, error) => {
console.log(
msg, url, row, col, error
);
return true;
}, true);

注意

window.onerror 只有在返回 true 时,异常才不会向上抛出, 原理是冒泡机制。而且 window.onerror 是无法捕获到网络异常的,因为网络请求异常不会事件冒泡,虽然能在捕获阶段捕获到,但是无法判断 HTTP 的状态码,需要配合服务端才能排查。
我们平时写 window.onerror 的时候,最好写在最顶端,以免前面的错误脚本阻断了。同时在 vue 或者 react 框架中,他们有自身的错误捕获机制,所以会掩盖 window.onerror,比如 vue 的 Vue.config.errorHandler。想详细了解看官网文档即可。

try…catch…finally
这种方式应该是我们平时用的最多的,具体功能想必大家也应该都很清楚,将我们需要检测的代码包裹在 try 和 catch 中,当发生语法错误或者其他错误时,就会从 catch 里捕获到,而不管前端发生了什么,最后 finally 里的代码总会执行。
下面的代码会怎么执行,大家可以思考一下
try {
try {
throw new Error(“oops”);
}
catch (ex) {
console.error(“inner”, ex.message);
throw ex;
}
finally {
console.log(“finally”);
return;
}
}
catch (ex) {
console.error(“outer”, ex.message);
}
特别注意

try…catch 只能捕获到运行时的非异步错误,而语法错误和异步错误就捕捉不到。
还有一点就是 try..catch 比较消耗性能,能少用的最好。

unhandledrejection
有些错误 try..catch 和 windows.onerror 也是无能为力的,比如说 Promise 实例从 pending 转变为 rejected 时,如果加了 catch 就会被捕获到,但要是没有加,那么继续抛出就会 Uncaught(in promise) Error

比如下面代码就捕获不到错误
window.onerror = function(message, source, lineno, colno, error) {
console.log(message)// 字符串错误信息
console.log(source)// 发生错误的脚本
console.log(lineno)// 发生错误的行号
console.log(colno)// 发生错误的列号
console.log(error)//Erroe 对象

return true// 将代码错误定格在捕获阶段
}

// 调用 Promise.reject 类方法
Promise.reject(‘promise error’);
// 在工厂方法中调用 reject 方法
new Promise((resolve, reject) => {
reject(‘promise error’);
});
// 在工厂方法或 then 回调函数中抛异常
new Promise((resolve) => {
resolve();
}).then(() => {
throw ‘promise error’
});
我们可以通过下面的方法监听到
window.addEventListener(‘unhandledrejection’, function (event) {
// …your code here to handle the unhandled rejection…

// Prevent the default handling (such as outputting the
// error to the console

event.preventDefault();
});
但是这方法的兼容性有点失望
What the heck is “Script error”?
‘Script error’这个错误是比较常见的,一般通过 windows.onerror 捕获到后基本确实就是哪个 script 文件有问题,而且这个文件还是跨域的,既然是跨域导致信息不全,所以首先要解决的就是跨域问题,关于跨域方面的前端解决方案可以看我这篇文章《前端常见跨域方案汇总 )》;
对于 script 标签,我们还需要额外配置一个参数 crossOrigin
<script src=’www.example.com’ crossorigin></script>
至于服务端,常用的就是
了解 sourceMap
对于这个功能的讲解,看阮大神的讲解 ) 是最适合不过的了,当然了解其基本设计思路也是很重要的,SourceMap 其实就是一个信息文件,存储着源文件的信息及源文件与处理后文件的映射关系。我们平时 vue 或者 react 项目开发中,通过 webpack 配置在测试环境中默认开启生成 SourceMap, 出现错误能够及时重现原代码,但是正式环境我们一般是不会将 SourceMap 文件发布上去的,但是正式环境的代码一般都是压缩过的,所以如果报错了,一般是很难 定位到原代码的位置,这时候优秀的错误上传功能,以及平台处理错误分析就显得尤为重要了。下图就是大概的设计思路。

Navigator.sendBeacon() 存在的意义
平时我们很常见网页卡顿或者直接崩溃,一般是通过 window 对象的 load 和 beforeunload 事件实现了网页崩溃的监控;具体看这篇文章,Logging Information on Browser Crashes

实现代码
window.addEventListener(‘load’, function () {
sessionStorage.setItem(‘good_exit’, ‘pending’);
setInterval(function () {
sessionStorage.setItem(‘time_before_crash’, new Date().toString());
}, 1000);
});

window.addEventListener(‘beforeunload’, function () {
sessionStorage.setItem(‘good_exit’, ‘true’);
});

if(sessionStorage.getItem(‘good_exit’) &&
sessionStorage.getItem(‘good_exit’) !== ‘true’) {
/*
insert crash logging code here
*/
alert(‘Hey, welcome back from your crash, looks like you crashed on: ‘ + sessionStorage.getItem(‘time_before_crash’));
}
但是上面的编码模式存在不友好的问题,当我们尝试在卸载页面前通过上传服务器数据,为了延迟页面卸载,需要通过同步 XMLHttpRequest 发送数据或者创建一个几秒的循环来延迟卸载。这样的处理可想而知不是很友好,这时候 sendBeacon() 就横空出世了,很简单的实现向服务器发送数据,同时又不会影响下一个页面的加载,具体如下面简单的代码实现。
window.addEventListener(‘unload’, logData, false);

function logData() {
navigator.sendBeacon(“www.youAddress.com”, analyticsData);
}
扩展阅读
7 天打造前端性能监控系统
成熟解决方案 badjs、sentry、raven-jsFunDebug)
如果大神您想继续探讨或者学习更多知识,欢迎加入 QQ 或者微信一起探讨:854280588

退出移动版