前言
最近一直在考虑做前端工程化的个人项目,考虑到整个前端开发链路上的各个环节,这其中包含很多可以工程化的流程,例如自动化测试、性能监测、自动化部署以及今天要讲的自动化监控平台。
一. 为什么要做监控平台
项目上线,总是能遇到一些匪夷所思的线上bug,然后这些问题还不好重现,需要通过客服去了解用户的机型、网络状况甚至操作流程,然后通过找到相同的机型想还原错误场景,最后发现完全没有问题!遇到这种情况,真的就是叫天天不应,叫地地不灵了~To B的项目,这种情况只能派内部工程师出差去客户现场解决问题,而To C的项目,然用户换个浏览器或者手机先试试,但是bug最终还是需要被解决掉的。线上的bug是最需要及时响应并解决掉的问题,但是作为开发者基于现有的条件根本无法做到快速定位问题所在,因此这也凸显出开发流程中有一套好的线上监控系统的重要性。
二. 监控系统架构
整体架构
如上图所示,整个系统分为三个流程:
- 监控的SDK:系统的核心组成,主要包含错误收集、数据处理和信息上报三个模块;
- Server端数据支撑:接收标准化错误信息数据的收集和数据存储,并做持久化处理,同时要包含预警通知系统;
- 可视化平台:错误信息的可视化展示,方便更清晰的查看和总结。
三. 前端异常收集
* 前端的异常包含很多种类,从源代码的语法错误、编译运行错误,再到AJAX请求报错、静态资源加载异常,任何一个不可控的前端异常,都能影响用户的使用体验,因此总结一下针对不同类型的异常的收集和处理:
DNS劫持
一般没有部署https的网站可能会发生DNS劫持的现象,而HTTP域下的劫持检测,其检测思路为请求Node层指定域名下的样本HTML或JavaScript资源,对比返回结果是否符合预期。针对这种异常,最好的处理方式就是网站部署HTTPS。
JS语法错误,运行异常
当 JS
运行时错误发生时,window
会触发一个 ErrorEvent
接口的 error
事件,并执行 window.onerror(),
监听全局的`onerror`事件
window.error = function (message, source, lineno, colno, error) { /* TO DO */
return true // 返回true,异常不会继续向上抛出,即阻止执行默认事件处理函数
}
但是问题在于`window.error`只能捕获运行时的同步错误,对于语法错误、资源加载异常和异步错误都无法捕获,因此需要其他的方式来处理这类异常。
静态资源的加载异常
对于图片或者其他CSS、JS静态资源,一旦加载错误,就会触发该元素的error事件,但是error事件并不会冒泡到window上面,因此只能在捕获阶段捕获这种类型的错误,目前Firefox浏览器上是可以通过`window.addEventListener`进行捕获:
window.addEventListener('error', function (err) {
/* TO DO */
}, true) // true为捕获阶段
AJAX请求异常
所有的ajax请求库都是基于`XMLHttpRequest`进行二次封装的,因此通过拦截原型上面的`open`和`send`方法,根据`status`判断异常请求进行处理:
window.XMLHttpRequest.prototype.send = function () {/* TO DO */}
window.XMLHttpRequest.prototype.open = function () {/* TO DO */}
Promise异常捕获
针对Promise种reject之后没有被catch的错误,有原生的事件可以支持捕获相应的错误:
window.addEventListener('unhandledrejection', function (e) {
/* TO DO */
e.preventDefault() // 需要阻止事件冒泡,拦截默认的事件处理器
})
script error跨域脚本异常
针对这种脚本报错,一般都为跨域脚本,因此需要在对应的`script`标签加上`crossorigin`属性,允许脚本跨域访问:
<script src="demo.js" crossorigin></script>
同时服务器端的请求头需要设置`Access-Control-Allow-Origin`,设置完成之后跨域脚本的异常就可以按照正常的JS一样被捕获。
页面崩溃或者异常
用于代码错误,导致运行时出现堆栈溢出,进而引发页面崩溃,此时JS都无法正常运行,需要通过下面的方式进行捕获:
window.addEventListener('load', function () { // 页面加载的事件 sessionStorage.setItem('good_exit', 'pending'); setInterval(function () { sessionStorage.setItem('time_before_crash', new Date().toString()); }, 1000);});window.addEventListener('beforeunload', function () { // 页面正常关闭卸载的事件 sessionStorage.setItem('good_exit', 'true');});if(sessionStorage.getItem('good_exit') && sessionStorage.getItem('good_exit') !== 'true') { /* insert crash logging code here */ alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));}
如上述代码所示,在页面正常加载访问的时候,记录一个sessionStorage状态用于记录当前页面的加载状态`pending`,用户正常退出更新这个状态为`true`,如果这个页面发生崩溃,则不会被`beforeunload`事件监听到,则再次进入访问原状态仍为`pending`,就可以判断页面发生崩溃。
未完待续......后面会更新,项目设计、打包工具和代码编写相关部分~
参考链接: