bug是不可能被全部测试出来的,由于成本和上线档期的考虑,测试无法做到“面面俱到”,即使时间充裕也总会有这样或那样的bug埋藏在某个角落。
此外,一个可靠的前端监控系统还可以化被动为主动,不再被动的等待客服来找,而是在问题出现时开发人员可以第一时间知道并解决。
可以捕获JavaScript错误的基本方法有以下三个:
try…catch
window.onerror
Promise.prototype.catch()
评价捕获错误方式的好坏可以从以下几个维度考虑:
下面来逐个对比上面三种方式。
try..catch
优点是可以返回完整的错误堆栈,缺点是无法捕获异步异常,看下面的代码:
// 同步异常的捕获
function foo () {
doSomething()
}
try {
foo()
} catch (e) {
console.log(e)
}
// 会打印出完整的错误堆栈
// ReferenceError: doSomething is not defined
// at foo (<anonymous>:2:3)
// at <anonymous>:6:3
// 异步异常的捕获
function onloadHandler () {
doSomething()
}
try {
window.onload = onloadHandler
} catch (e) {
// 获取不到任何信息!
}
除了对于异步异常无法捕获之外,try…catch
势必也会修改源代码,如果想通过try…catch
捕获到全部异常的话,要在大量的代码中追加try…catch
处理。
window.onerror
的优点可以捕获同步和异步的异常,但是它受到同源策略的限制,只能捕获当前域名下的错误。
比如下面的情况:
// @file: example1.com/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<script>
window.onerror = function (...args) {
console.log('--------------')
console.log(args)
console.log('--------------')
}
</script>
<script src="http://example2.com/main.js"></script>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
...
</body>
</html>
exampl2.com/main.js
中如果出现异常,window.onerror
虽然会被触发,但是回调获取的信息没有任何参考价值,内容如下:
["Script error.", "", 0, 0, null]
0: "Script error."
1: ""
2: 0
3: 0
4: null
这是浏览器出于安全考虑,禁止了获取跨域脚本的错误信息。但是可以通过在<script>
中设置crossorigin
为anonymous
来解决,同时服务器端也要做出修改,设置responese headers
Access-Control-Allow-Origin:*
。但是要注意,关于crossorigin
也有他的局限性,并不是所有浏览器都完美支持,下面是各浏览器支持的情况:
图片来自《把前端监控做到极致》
利用Promise.prototype.catch()
可以捕获Promise实例中发生的异常。
看下面的例子:
function queryData () {
return myFetch('example.com/user?id=2393')
.then(response => {
// response.msg并不存在,所以对其调用toLowerCase()方法会报错
console.log(response.msg.toLowerCase())
})
}
//由于queryData()返回的是一个promise实例,所以可以用catch对异常进行捕获
queryData().catch(error => {
console.log(error)
})
缺点是只能捕获promise内部的错误。
通过上面三种方式的对比,貌似没有哪个可以作为完美的解决方案。
不过我们还可以看看其他解决方案。
现在市面上已经有很多比较成型的解决方案。
Vue
提供的错误处理回调——Vue.errorHandler
(无法捕获异步异常)onError
(异步、同步都可以捕获)。sentry
和bugsnag
根据上面的诸多对比,如果你的web应用是移动端的话,window.onerror是不错的选择。因为移动端的浏览器版本更高,支持性更好。
如果想做一个前端监控平台的话,你需要完成以下几个目标:
SDK
的形式进行安装,对应用代码尽量不做改动。第5点完备性比较依赖于前端监控平台的“消费端”,即不同的应用场景对于完备性有不同程度上的要求。但是前4点应该所有前端监控平台有着大致相同的需求。
篇幅有限,不能面面俱到,只提关键的几点。
前端打包,上报错误的行号
是经过编译压缩后的文件行号,所以为了方便分析,一定要在打包时生成sourceMap
,方便后续的分析。
错误上报,采用image ping
的方式,即将上报服务器地址当做为image url,将要上报的参数编码成url参数加载image url后面。这样做的原因如下:
image ping
可以跨域服务端,利用Nginx将上报信息转发到日志系统,不要做任何其他应用来进行“信息转译再存储”的动作,因为错误日志的数据量是庞大的,这样会消耗服务端的性能。
分析端,定期将日志转译、归并再存储到数据库中,然后再做分析处理(离线分析)。因为分析端(或者称“消费端”),是希望可以做到快速响应的,所以离线分析是个不错的方案。
图片来自《把前端监控做到极致
如果你已经部署了一套稳定的前端监控系统之后,你会发现bug的数量是无法想象的。大数据处理是个难点。
今后如果有时间,我会整理一下关于如何处理庞大的错误日志。(挖个坑~~?)
参考文章: