背景
最近在做前端監(jiān)控醋旦,其中對(duì)JS錯(cuò)誤需要上報(bào):錯(cuò)誤消息恒水、錯(cuò)誤文件、行饲齐、列钉凌、錯(cuò)誤棧。需要通過(guò)上報(bào)的錯(cuò)誤文件捂人、行御雕、列進(jìn)行源代碼的還原。
面臨的挑戰(zhàn)
乍一聽(tīng)滥搭,感覺(jué)也沒(méi)什么酸纲,思路已經(jīng)在腦海中:
1、window.onerror捕獲事件隊(duì)列中的錯(cuò)誤
2瑟匆、window.onunhandledrejection捕獲沒(méi)有catch的Promise報(bào)錯(cuò)
3闽坡、<script crossorigin="anonymous"></script>,通過(guò)script標(biāo)簽設(shè)置跨域?qū)傩詠?lái)捕獲跨域錯(cuò)誤
4愁溜、React通過(guò)componentDidCatch捕獲子組件的錯(cuò)誤信息
5疾嗅、Vue.config.errHandler獲取vue項(xiàng)目的error對(duì)象
通過(guò)以上方法獲取錯(cuò)誤文件、行冕象、列宪迟,再通過(guò)source-map還原源代碼。
想法很美好交惯,我們的業(yè)務(wù)主要是Vue框架,errorHandler用法如下
Vue.config.errorHandler = function (err, vm, info) {
// handle error
}
Vue通過(guò)try{}catch(err){}獲取錯(cuò)誤穿仪,然后把catch得到的err對(duì)象傳遞給errorHandler席爽。
問(wèn)題來(lái)了,catch中的error對(duì)象是沒(méi)有行啊片、列信息的只锻,測(cè)試打印如下
(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
只有錯(cuò)誤消息和錯(cuò)誤棧,沒(méi)有行和列就還原不了源代碼了紫谷。
思路
window.onerror有行和列信息齐饮,怎么讓它接收所有錯(cuò)誤呢?
window.onerror = function(message, file, line, col, error) { ... }
在同步JS中笤昨,throw err祖驱,可被window.onerror捕獲,如下:順著這個(gè)思路捺僻,在Vue的errorHandler中把錯(cuò)誤對(duì)象拋出,被window.onerror捕獲,這樣就能得到錯(cuò)誤的行匕坯、列束昵。
對(duì)errorhandler改造如下:
window.Vue.config.errorHandler = function (err) {
throw err;
}
但是報(bào)錯(cuò)沒(méi)有進(jìn)入window.onerror并打印
這時(shí)查看Vue源碼得知,直接在errorHandler拋出錯(cuò)誤會(huì)被Vue的try...catch捕獲葛峻。那我們需要改造下代碼如下:
window.Vue.config.errorHandler = function (err) {
setTimeout(() => {
throw err;
})
}
通過(guò)setTimeout把錯(cuò)誤在下一個(gè)宏任務(wù)拋出锹雏,這時(shí)就能被window.onerror捕獲了,如下圖:
總結(jié)
JS錯(cuò)誤需要上報(bào)錯(cuò)誤文件术奖、行礁遵、列信息的場(chǎng)景,都可以通過(guò)throw error的方式腰耙,被window.onerror接收然后解析出行榛丢、列信息,為還原源代碼提供必要參數(shù)挺庞。