点击「百度外卖技术团队」快速关注
点击「百度外卖技术团队」快速关注
JavaScript作为当前web前端主力语言,承担着非常重要的责任,用户体验与其息息相关,但作为一个脚本语言,它有着天然的弱点,弱类型带来的类型检查导致无法提前发现异常,特定的用户环境、特定的设备、特定的网路以及特定的用户都可能造成JavaScript的异常。本文主要讲述如何实现一个包含异常捕获、异常上报、异常日志收集以及异常报表可视化展示的建异常监控平台。
浏览器端提供了用于捕获异常的方式window.onerror
如上所示,一个简单的上报方案就完成了。但还很不完善,msg提供的信息不够丰富:Uncaught ReferenceError: c is not defined 这样的信息还不足以帮助我们追踪异常。
注: onerror兼容性有问题,ie10 & safari6 才完整支持以上全部参数。
这时候我们发现error.stack可以追踪异常栈,异常栈有很详细的信息来告知我们的发生异常的顺序以及函数细节:
无stack兼容方案:
部分浏览器无stack , PC端:ie10 & safari6 才支持stack,移动端:Android Browser4 && Safari Mobile6 才支持stack。
我们的方案是利用arguments.callee.caller递归得出调用堆栈, 不过严格模式下不可用。
支持自定义上报异常(常常与`try...catch`配合):
现代的大中型网站中,静态资源一般都是CDN部署方式,存放在另外的域名下,与使用该资源的网站不属于同域。
当引用的非同域JS文件中产生异常以后,会得到一个『Script error.』异常,无法得到更多信息,这属于浏览器做的一个限制。
解决方法:
CDN资源头设置Access-Control-Allow-Origin: *
<script src="https://xxx.com/x.js" crossorigin></script>脚本新增crossorigin属性。
在这里我们学习了一些框架中的做法,做简单的hook
1.6 其他捕获问题通过hookXMLHttpRequest的原型方法以及监听readystatechange方法可以监控资源请求异常状况,进行上报操作。
通过以上方式,我们已经可以捕获大部分JavaScript异常了,不过呢,作为一个特别的语言,js还有很多异常无法通过 window.onerror来直接捕获到,还有一些工作需要做:
setTimeout/setInterval/requestAnimationFrame 异常捕获
业务入口包裹try…catch 来截获异常
define/require 入口包裹
fetch 请求hook
监控客户端也就是一段js脚本,页面中嵌入js的方法就两种,一种是内联,一种是外链。但是,这两种方案都不能满足我们下面的需求:
性能问题:希望监控能尽量减小对页面性能的影响,加载尽量延后,资源尽量走缓存,没有缓存走cdn。
缓存的问题:作为一个线上的产品,一定要做到上下线可控制。新迭代上线一定要低延迟或者0延迟的更新到所有产品线,但是又希望没有更新的时候尽量走缓存。
使用的便捷性:每次迭代上线不需要各个业务方上线,统一升级。
如果外链就没办法同时解决性能和缓存的问题,如果用内联的方式又没发解决业务方使用的便捷性。
我们采用了两者结合的方式,取长补短:
异步加载客户端:监控客户端是页面load之后通过内联代码异步加载,避免对页面加载性能的影响。
loader机制解决缓存问题:所有的客户端都是通过一个超小体积的loader加载,loader永不缓存,客户端有更新就更新对应客户端的地址,没有更新地址不变,就会走缓存。
统一升级:通过以上机制,监控的升级也不需要业务方再参与了
细心的同学会发现,如果有异常在load事件之前抛出,是不能捕获的,所以我们的内联代码还有一个功能就是暂存日志。客户端加载完成之后会重放一遍。
异常请求的部署完监控客户端之后就可以进行异常上报了,异常上报是通过加载一张图片去触发一个http请求,异常信息序列化之后放在请求的参数中,后端收到请求后产出日志,然后通过一系列的操作把日志存储到数据库中以供查询。
为了支持实时报表和巨大的数据的量级,后端架构如下
这个架构可以做到大量级的日志实时处理,也有很好的扩展性
出现次数最多的异常就是对页面影响最严重的,异常列表默认会把相同的异常聚合,然后按照每种异常的出现次数降序展示。
那么,怎么定义相同的异常?在同一文件同一行同一列抛出的,并且调用栈相同的异常就是同一个异常。
监控客户端会记录发生异常时的页面地址,异常发生的文件,行号,列号,异常发生时的调用栈。这些信息都会在报表里展示,但是还不够,通常线上的代码都是压缩过的,根据动辄5位数的列号去寻找异常位置是非常困难的,所以我们开发了直接查看源码的功能
研发不管是用webpack还是fis压缩代码都可以产出sourcemap,把产出的sourcemap上传到我们的平台之后,再点击调用栈的时候,就可以看到对应调用栈的源代码,如下图
我们还开发了sourcemap上传组件,不需要在浏览器端点击,在编译完成后执行一个脚本就可以上传。
每次异常发生时,监控客户端出了异常的上下文信息之外,也会记录异常发生时的外部环境包括屏幕分辨率、dpr、useragent等,这些信息都会在报表有展示,方便研发复现异常
总量变化可以按照日周月的维度查看,可以很方便的知道当前产品线的健康度趋势,如果异常pv增长过大就要考虑是不是最近的上线引入了bug,如果减小了就是异常修复有效果了。
硕哥,百度外卖新品类产品部商超方向前端负责人,负责商超前端架构设计和性能优化,主导研发超市购金刚的hybird 应用、前端监控项目等。
新品类前端团队,肩负了百度外卖新业务产品的开发,拥有最多的H5开发产品线,在支撑业务高速发展的同时,积累和沉淀了一系列基础框架、基础平台和技术方案的最佳实践,期待未来能和业界有更多的交流。
欢迎关注百度外卖技术团队
点击【阅读原文】加入百度外卖技术团队!