背景
最近在做前端監控,其中對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接收然后解析出行、列信息,為還原源代碼提供必要參數。