聊聊前端监控(三)--异常监控的实现

前两篇已经总结了前端监控的主要使用场景和行为监控的实现方式,这一篇,主要来聊一下异常监控的实现方式。

总体思路

和行为监控的设计的思路一致,无侵入也是最值得思考的系统设计重点。和前面一样,由于篇幅,这里只简单的谈一谈设计实现,如果需要了解细节,可以留言一起探讨。在本文最后,会说明一下,当捕获到错误之后的上报的方法。

全局异常

全局异常是异常漏斗的最下一层,基本上用于捕获抛到window.onerror 事件里面捕获的异常,同时采用这种方式捕获的异常最简单的方式,一般异常捕获系统都会实现一个托底异常捕获。具体实现方式如下:

    // 覆盖window.onerror函数及注意点
    window.onerror = function(message, source, lineno, colno, error) {
        // Script error 不需要上报,因为同源限制,上报了也没有意义
        if("Script error."=== messaage && !source) return false;
        // 这里要注意用setTimeout包装一下,防止报错太多,卡住主线程
        setTimeout(function(){
         // do something 上报
        })
    }

重写window.onerror函数,确实可以使异常监控有一定的托底作用,但是受到同源等安全策略的限制,很多异常不会如我们预期的一样报错,即使上报上来也没有什么意义。如果报上来都是没有意义的数据,那就无端增加了监控系统的消耗。

这里也提供另一个全局捕获思路,来自于 sentry raven.js,可以参看 _instrumentTryCatch 函数,基本思路是通过包装各类事件,来捕获事件异常,构造自己的异常捕获系统,这里就不再展开了,有兴趣的同学可以自己阅读相关源码。这种方式相比直接重写window.error更加先进,但是也更加复杂,好在有sentry帮忙维护了。

总的来说,通过全局sdk的方式,我们可以简单直接的获取一层托底异常。但是可能有几个问题:

  • 1、代码中有try{}catch(e){}类的异常不会抛到最外层,window.onerror无法捕获错误
  • 2、有些需要自定义的上报信息无法及时捕获

自定义异常上报

为了解决以上全局上报的问题,自定义异常上报作为全局上报系统的补充,是各个监控系统必不可少的功能,这个功能的完善与否,直接决定这框架的好坏。

一般来说,自定义上报都会提供如下信息:

  • 1、自动增加上报时间
  • 2、自动增加用户信息
  • 3、可配置增加错误信息
  • 4、用户上报的信息

值得一提的是,自定义上报信息的解析很多监控系统的后端做法都不一样,有些是让用户导出数据,自己格式化,拆解做分析,更智能一些的是让用户上传结构数据,然后对结构智能拆解,输出报表。个人觉得没有什么优劣,智能拆解相对来说使用成本变低,但是易用性要求和开发难度也会相应变高。

异常分级

写过后端代码的同学都知道,异常一般分为很多级别的,对于各种级别和类型的异常处理方式也不一样。普遍来说,一般 info,warn,error 几个级别都是有的。

这里主要说下error类错误的处理方式,一般来说,error类错误会实时上报后端,因为和监控系统联动的告警系统很需要这类实时数据,以便及时告警。其他类型的错误,如果不是致命类异常错误,可能会用到离线(空闲)上报的策略,以减少消耗,提高性能。

实时上报

上节说到,上报方式有两种,这一节介绍下实时上报的方式。实时上报本质上就是系统的一个sendMessage的方式,一般来说,只要构造一个get请求就可以了。

举个例子:

    // 使用image的方式send info
    function sendInfo(url) {
        var _image = new Image(1, 1);
        // 挂载到监控全局上,保证同时只有一个,减少消耗
        monitor.img=_image;
        // 发送完成后清除掉相关函数,离线上报需要改造一些
        _image.onload = _image.onerror = _image.onabort = function() {
            _image.onload = _image.onerror = _image.onabort = null;
            monitor.img = null;
        }
    }

以上,一般的系统上报方式大同小异,本质上原理是提一个发后不理的get请求,但是由于浏览器的并发数量限制,如果上报触发太多的话,有可能会影响宿主系统的性能。为了解决这个问题,离线(空闲)上报方式就诞生了。

离线上报

离线上报主要是为了解决两个问题:

  • 1、上文提到的宿主系统性能的问题
  • 2、补充一些失败的日志,这儿要在上文提到的上报部分做一些改造

代码比较多,这里就提一下思路先:对于非error类的需要实时收集的异常,可以采用本地存储的方式存储起来,寻机上传。至于寻机,可能是根据网络和系统的情况变化,比如从弱网情况恢复到良好网络情况,可以统一上传,这里注意上传时的节奏即可。这儿还会有一些退化重试的上传的时间间隔的算法。

总的来说,就是不必要实时上报、当前无法上报、上报失败的数据可以先存起来,择机上报。至于存在哪里,什么时候上报,是不是可以手动触发,看各个系统自己的实现了。

综上,异常监控的基本已经说清楚了,下一篇看看聊聊性能监控的实现。

Table of Contents