背景
最近在做前端监控,其中对JS错误需要上报:错误消息、错误文件、行、列、错误栈。需要通过上报的错误文件、行、列进行源代码的还原。
面临的挑战
乍一听,感觉也没什么,思路已经在脑海中:
1、window.onerror捕获事件队列中的错误
2、window.onunhandledrejection捕获没有catch的Promise报错
3、<script crossorigin="anonymous"></script>,通过script标签设置跨域属性来捕获跨域错误
4、React通过componentDidCatch捕获子组件的错误信息
5、Vue.config.errHandler获取vue项目的error对象
通过以上方法获取错误文件、行、列,再通过source-map还原源代码。
想法很美好,我们的业务主要是Vue框架,errorHandler用法如下
Vue.config.errorHandler = function (err, vm, info) {
// handle error
}
Vue通过try{}catch(err){}获取错误,然后把catch得到的err对象传递给errorHandler。
问题来了,catch中的error对象是没有行、列信息的,测试打印如下
(function foo() {
try {
aa.bb
} catch(err) {
console.log(err)
}
})()
VM327:5 ReferenceError: aa is not defined
at foo (<anonymous>:3:9)
at <anonymous>:7:3
只有错误消息和错误栈,没有行和列就还原不了源代码了。
思路
window.onerror有行和列信息,怎么让它接收所有错误呢?
window.onerror = function(message, file, line, col, error) { ... }
在同步JS中,throw err,可被window.onerror捕获,如下:顺着这个思路,在Vue的errorHandler中把错误对象抛出,被window.onerror捕获,这样就能得到错误的行、列。
对errorhandler改造如下:
window.Vue.config.errorHandler = function (err) {
throw err;
}
但是报错没有进入window.onerror并打印
这时查看Vue源码得知,直接在errorHandler抛出错误会被Vue的try...catch捕获。那我们需要改造下代码如下:
window.Vue.config.errorHandler = function (err) {
setTimeout(() => {
throw err;
})
}
通过setTimeout把错误在下一个宏任务抛出,这时就能被window.onerror捕获了,如下图:
总结
JS错误需要上报错误文件、行、列信息的场景,都可以通过throw error的方式,被window.onerror接收然后解析出行、列信息,为还原源代码提供必要参数。