Skip to content
LYF edited this page Aug 18, 2016 · 4 revisions

调调前端异常监控

除了监控客户端的异常之外,还监控图片404

经过真机测试:

IE8下可以上报0,3

在IE9+及其他现代浏览器中能上报所有类型的异常

相比较 reporter 进一步进行了简化,删除了不必要的方法和逻辑。

删除script标签更健壮了。

 /**
  * 前端异常监控
  */
 
 /**
  * 0 代码错误
  * 1 主动上报的错误
  * 2 主动上报的warn
  * 3 图片挂了
  */
 (function(window) {
     'use strict';
     /**
      * 1、减少传数量,在后台拿出UA即可
      * 没必要在前端分析操作系统、浏览器、浏览器版本
      * 
      * 2、减少传数量,在后台拿到refer,这样发生异常的url也不用传输了
      */

     /*
        原生ajax函数。用法与jquery ajax一致。
        author:pod4g
        修改时间:2016,02,20
        版本:1.1
        https://github.com/pod4g/qAjax
    */
     // TODO: 作为上报组件的一个功能。简化成只支持JSONP即可
     // 这样就支持IE8+以上的上报了。
     function request(opts) {
         var
             data = opts.data || {}, // 请求参数
             prop2prame = function(data) {
                 var ret = "", prop;
                 if (!data || typeof data !== 'object') {
                     return ret;
                 }
                 for (prop in data) {
                     ret += prop + '=' + data[prop] + '&';
                 }
                 return ret;
             };
         // 尽可能地缩短传输参数。
         // 在IE8 下的URL地址总长度为:4076,超过该长度会自动忽略后面的内容;
         // 在firefox 25下的URL地址总长度可以达到:7530,超过该长度会访问错误;
         // 在chrome 29.0.1547.62 的最大总长度达到:7675,超过该长度会访问错误;
         // var callbackParamValue = '_cb__' + Math.floor(Math.random() * 10000);
         // var url = '//120.27.45.36:3003/error_reciver?' + prop2prame(data);
         // var param = prop2prame(data);
         // console.log(param);
         // b=_cb__4553
         // 原来key是c
         // 导致和错误信息中的 c 即列号冲突
         // 在server端拿到的c为["9","_cb__4553"]
         // 导致insert到数据库中报语法错误
         // param = param.concat('b').concat('=').concat(callbackParamValue);
         // if ( url.indexOf('?') == -1 ) {
             // param = '?' + param;
         // } else {
             // param = '&' + param;
         // }

         (function(src) {
             var __script__ = document.createElement('script');
             __script__.type = 'text/javascript';
             __script__.src = src;
             __script__.onerror = __script__.onload = function(e){
                 removeChild(__script__);
                 __script__ = undefined;
             }
             document.body.appendChild(__script__);

             // 默认超时时间为9秒
             setTimeout(function() {
                 removeChild(__script__);
             }, 9000);
         })( '//x.x.x.x:3003/error_reciver?' + prop2prame(data) );
     }

     function removeChild(node){
        if(!node) return;
        var f = document.createDocumentFragment();
        f.appendChild(node);
        f.removeChild(node);
     }

     function addEvent(obj, event, handler) {
         if (obj.addEventListener) {
             obj.addEventListener(event, handler);
         } else if (obj.attachEvent) {
             obj.attachEvent('on' + event, handler);
         }
     }

     function isFunctionOrObject(obj) {
         var type = typeof obj;
         return type === 'function' || type === 'object';
     }

     function encode(str){
        return encodeURIComponent( str || '' );
     }

     /**
      * 传输的参数格式为:
      *   {
      *     t: 0,1,2,3 // type
      *     m: 'error info' // message
      *     f: 'xxx.js' // filename
      *     l: 119 // lineno
      *     c: 120 // colno
      *   }
      */

     // hook console
     (function() {

         function errorHook(msg) {
             var type = typeof msg,
                 err = {
                    "t": 1
                 };

             if (type === 'string' || type === 'number') {
                 err.m = encode(msg);
             } else if (type === 'object') {
                 err.m = encode(msg.message);
                 err.f = msg.filename || '';
                 err.l = msg.lineno || '';
                 err.c = msg.colno || '';
             }
             // console.log('触发主动上报error' + JSON.stringify(err));
             request({ data: err });
         }

         function warnHook(msg) {
             // console.log('触发主动上报warn' + JSON.stringify(warn));
             // request({url:'//120.27.45.36:3003/error_reciver', data:JSON.stringify({type:'warn',message: msg})};
             request({data: {
                 "t": 2,
                 "m": encode(msg)
             }});
         }

         var console = window.console;

         if (console) {
             var oError = console.error,
                 oWarn = console.warn;
             if (isFunctionOrObject(oError)) {
                 console.error = function(msg) {
                     try {
                         oError.call(console, msg);
                         // console.log('执行errorHook');
                         errorHook(msg);
                     } catch (e) { // 必须有参数,在IE8下会报 “缺少标识符” 错误
                         // errorHook(msg);
                     }
                 }
             }
             if (isFunctionOrObject(oWarn)) {
                 console.warn = function(msg) {
                     try {
                         oWarn.call(console, msg);
                         warnHook(msg);
                     } catch (e) { // 必须有参数,在IE8下会报 “缺少标识符” 错误
                         // warnHook(msg);
                     }
                 }
             }
         } else {
             window.console = {
                 error: function(msg) {
                     errorHook(msg);
                 },
                 warn: function(msg) {
                     warnHook(msg);
                 }
             }
         }
     }());


     addEvent(window, 'error', function(e, sUrl, sLine) {
         // e = e || event;
         // alert(e);
         // 代码错误 type: 0
         // ie8-
         var error;
         if(
            // ie8- 
            typeof e === 'string'
          ){
            error = {
                 t: 0,
                 m: encode(e),
                 l: sLine
            }
         } 
          else  // w3c
         {
            error = {
                 t: 0,
                 m: encode(e.message),
                 f: e.filename,
                 l: e.lineno,
                 c: e.colno
            }
         }
        
         // console.log('应该上报一次,代码执行异常类型。信息:', JSON.stringify(error));
         // e.preventDefault(); // 不报错
         if(error){
            request({ data: error });
         }
     });

     addEvent(window, 'load', function() {
         var imgs = document.getElementsByTagName('img'),
             brokens = [],
             img, i = 0;
         while (img = imgs[i++]) {
             if (!img.complete || (img.naturalWidth === 0 && img.naturalHeight === 0)) {
                 brokens.push(img.src);
             }
         }
         // console.log('应该上报一次,图片挂了。信息:', JSON.stringify(brokens));
         // 图片挂了 type: 3
         if(brokens.length > 0 ){
             request({ data: {
                t:3,
                m: encode(brokens.join(','))
             }});
         }
     });
 }(window));
Clone this wiki locally