前者代码卓殊监察和控制实战,详解js前端代码非凡监察和控制

=====================================================================

何以要做前端格外监察和控制

  • 稍稍标题只存在于线上一定的条件
  • 后端错误有监察和控制,前端错误没有监督

初稿出处: happylindz   

读书目录

*前端质量监察和控制体系:* 预览地址

大旨完毕

参照大家nodejs服务层和app的不当反馈处理,基本流程为:
触发错误->捕获错误->错误报告->记录日志文件->存入ELK方便查询

  • 谬误捕获:

    1. 接口请求error
    2. 全局监听很是
    3. 当仁不让抓获
  • 谬误报告:
    此地是亟需大家参照app的地方,我们有3个日志服务器专门接收记录日志。

    那边必要大家的报告数据格式和日志服务器一致。

  • 记录日志文件和存入ELK
    我们依据反映的日记数据中八个key生成区别的日志文件,然后将ELK和那些日志文件绑定能够有利于大家在kibana上查询。

基于基本流程,能够分明大家各个部分的职分:

  • 前端

    • 报错事件监听
    • 报错处理举报
  • 后端

    • 提供接口收集报错
    • 积存入ELK方便查询

前言

从前在对卖家的前端代码脚本错误进行排查,试图降低 JS Error
的错误量,结合自身前边的经历对那方面内容开始展览了实施并总括,下边就此谈谈自身对前者代码非凡监察和控制的一对理念。

本文大概围绕下边几点展开钻探:

  1. JS 处理分外的主意
  2. 举报方式
  3. 老大监察和控制上报常见难题
  • 怎么样是前者代码卓殊 
  • window.onerror
  • 写三个js报错的上报库
  • 注意点:
  • 缺点:

=====================================================================

料想难点

  • 前者错误分类
  • 哪些调查出错地方
  • 错误怎样举报
  • 不当消息数据结构
  • 什么平滑的选取在事情连串中

JS 极度处理

对于 Javascript 而言,大家面对的仅仅只是非常,极度的出现不会一贯导致 JS
引擎崩溃,最两只会使近期实践的天职终止。

  1. 当下代码块将作为2个职务压入任务队列中,JS
    线程会不断地从职责队列中领取职务执行。
  2. 当职分执行进度中出现万分,且分外没有捕获处理,则会直接本着调用栈一层层向外抛出,最后平息当前任务的执行。
  3. JS 线程会继续从职分队列中领取下二个任务继续执行。
JavaScript

<script> error console.log('永远不会执行'); </script>
<script> console.log('我继续执行') </script>

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5a707ba987416418324373-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba987416418324373-2">
2
</div>
<div class="crayon-num" data-line="crayon-5a707ba987416418324373-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba987416418324373-4">
4
</div>
<div class="crayon-num" data-line="crayon-5a707ba987416418324373-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba987416418324373-6">
6
</div>
<div class="crayon-num" data-line="crayon-5a707ba987416418324373-7">
7
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5a707ba987416418324373-1" class="crayon-line">
&lt;script&gt;
</div>
<div id="crayon-5a707ba987416418324373-2" class="crayon-line crayon-striped-line">
  error
</div>
<div id="crayon-5a707ba987416418324373-3" class="crayon-line">
  console.log('永远不会执行');
</div>
<div id="crayon-5a707ba987416418324373-4" class="crayon-line crayon-striped-line">
&lt;/script&gt;
</div>
<div id="crayon-5a707ba987416418324373-5" class="crayon-line">
&lt;script&gt;
</div>
<div id="crayon-5a707ba987416418324373-6" class="crayon-line crayon-striped-line">
  console.log('我继续执行')
</div>
<div id="crayon-5a707ba987416418324373-7" class="crayon-line">
&lt;/script&gt;
</div>
</div></td>
</tr>
</tbody>
</table>

澳门葡京 1

在对脚本错误进行申报此前,大家须求对特别举行处理,程序必要先感知到脚本错误的产生,然后再谈分外申报。

脚本错误一般分为两种:语法错误,运营时不当。

下边就商讨三种格外监察和控制的处理格局:

在平日的劳作,js报错是相比宽泛的三个光景,特别是有一对错误或然我们在该地质衡量试的时候测试不出去,当公告到线上从此才能够发现,假若抢救及时,那万幸,假若很晚才发

  对于前端采纳来说,Js错误的发生间接影响前端选用的成色。对前者卓殊的监察和控制是整套前端监控系统中的七个主要环节。
那么哪些做到对Js错误的督察呢?对采访到的js错误,我们该怎么去分析呢?分析的结果该怎样展现呢?这几个标题将直接关系到是还是不是能够监察和控制和分出去有价值的数据。

荒唐分类

  1. javascript异常
    • 语法错误
    • 运作时不当
    • script文件内错误(跨域和未跨域)
  2. JS文件、CSS文件、img图片等(财富)的404荒谬(其实是有onerror事件的dom)
  3. promise的不得了捕获
  4. ajax请求错误

try-catch 格外处理

try-catch 在大家的代码中日常看看,通过给代码块实行 try-catch
实行包装后,当代码块发生出错时 catch
将能捕捉到错误的信息,页面也将得以继续执行。

不过 try-catch
处理非凡的力量简单,只好捕获捉到运营时非异步错误,对于语法错误和异步错误就突显心有余而力不足,捕捉不到。

现,那就或许引致十分大的损失了。若是大家前端能够监督到那种报错,并当即举报的话,那我们的标题就相比较好解决了。所以我们明日来聊天前端代码的充足监察和控制

  首先,咱们应该对Js报错意况有个大概的问询,那样才能够即时的垂询前端项目标健康情状。所以大家须求分析出一部分必要的数量。

荒唐捕获

  1. 继续努力抓获(try catch / promise catch)
  2. 大局捕获(onerror / add伊夫ntListener)

以身作则:运转时不当

JavaScript

try { error // 未定义变量 } catch(e) { console.log(‘小编了然不当了’);
console.log(e); }

1
2
3
4
5
6
try {
  error    // 未定义变量
} catch(e) {
  console.log(‘我知道错误了’);
  console.log(e);
}

澳门葡京 2

而是对于语法错误和异步错误就捕捉不到了。

哪些是前者代码卓殊 

  如:一段时间内,应用JS报错的增势(chart图表)、JS错误发生率、JS错误在PC端发生的概率、JS错误在IOS端爆发的概率、JS错误在Android端发生的票房价值,以及JS错误的归类。

当仁不让抓获

我们在一部分运算之后,获得多个愿意的结果,但是结果不是大家想要的,那时能够上报一下不当。
差不离主动抓获也正是讲求大家调用Logger.error(error, tag, message)(那几个是前者监察和控制js文件提供的一个方法)方法主动举报

演示:语法错误

JavaScript

try { var error = ‘error’; // 大写分号 } catch(e) {
console.log(‘作者感知不到错误’); console.log(e); }

1
2
3
4
5
6
try {
  var error = ‘error’;   // 大写分号
} catch(e) {
  console.log(‘我感知不到错误’);
  console.log(e);
}

澳门葡京 3

相似语法错误在编辑器就会反映出来,常表现的错误音信为: Uncaught
SyntaxError: Invalid or unexpected token xxx
那样。然则那种错误会直接抛出十二分,常使程序崩溃,一般在编码时候简单观望得到。

诚如语法错误以及运维时不当,浏览器都会在console里边体现出错误音信,以及出错的文书,行号,堆栈音信。

  然后,大家再去当中的Js错误进行详尽的辨析,帮助大家排查出错的岗位和产生错误的原委。

try catch 捕获
  • 务求程序员在编写代码时,注意基本的足够捕获
  • 自行对JavaScript函数块添加分外捕获应用UglifyJS,使用AST为全体函数加上try
    catch

try {} catch(err) {}没辙捕捉到异步错误和语法错误

自动添加try
catch仅能对js文件生效,不能够对html文件举行操作。(可以在catch中报告有关代码地点)

以身作则:异步错误

JavaScript

try { setTimeout(() => { error // 异步错误 }) } catch(e) {
console.log(‘小编感知不到不当’); console.log(e); }

1
2
3
4
5
6
7
8
try {
  setTimeout(() => {
    error        // 异步错误
  })
} catch(e) {
  console.log(‘我感知不到错误’);
  console.log(e);
}

澳门葡京 4

除非您在 setTimeout 函数中再套上一层
try-catch,不然就无法感知到其错误,但那样代码写起来比较啰嗦。

我们先来说手前端代码相当是何等看头。前端代码很是指的是以下三种情况:

  如:JS错误类型、
JS错误音信、JS错误堆栈、JS错误产生的任务以及有关职分的代码;JS错误发生的概率、浏览器的品类,版本号,设备机型等等援助讯息

大局捕获

window.onerror 分外处理

window.onerror 捕获极度能力比 try-catch
稍微强点,无论是异步照旧非异步错误,onerror 都能捕获到运营时不当。

以身作则:运转时1只错误

JavaScript

/** * @param {String} msg 错误音信 *前者代码卓殊监察和控制实战,详解js前端代码非凡监察和控制。 @param {String} url 出错文件 *
@param {Number} row 行号 * @param {Number} col 列号 * @param {Object}
error 错误详细音信 */ window.onerror = function (msg, url, row, col,
error) { console.log(‘笔者清楚不当了’); console.log({ msg, url, row, col,
error }) return true; }; error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @param {String}  msg    错误信息
* @param {String}  url    出错文件
* @param {Number}  row    行号
* @param {Number}  col    列号
* @param {Object}  error  错误详细信息
*/
window.onerror = function (msg, url, row, col, error) {
  console.log(‘我知道错误了’);
  console.log({
    msg,  url,  row, col, error
  })
  return true;
};
error

澳门葡京 5

示范:异步错误

JavaScript

window.onerror = function (msg, url, row, col, error) {
console.log(‘小编清楚异步错误了’); console.log({ msg, url, row, col, error
}) return true; }; setTimeout(() => { error; });

1
2
3
4
5
6
7
8
9
10
window.onerror = function (msg, url, row, col, error) {
  console.log(‘我知道异步错误了’);
  console.log({
    msg,  url,  row, col, error
  })
  return true;
};
setTimeout(() => {
  error;
});

澳门葡京 6

不过 window.onerror
对于语法错误依然无能为力,所以我们在写代码的时候要尽量防止语法错误的,可是貌似那样的荒唐会使得全部页面崩溃,照旧比较便于能够发现到的。

在实质上的选用进程中,onerror 重假释迦牟尼捕获预料之外的不当,而 try-catch
则是用来在可预知情状下监察和控制特定的谬误,两者结合使用进一步快捷。

亟需小心的是,window.onerror 函数唯有在回到 true
的时候,十分才不会发展抛出,否则纵然是精晓相当的发生控制台仍然会显得
Uncaught Error: xxxxx。

澳门葡京 7

关于 window.onerror 还有两点需求值得注意

  1. 对此 onerror 这种全局捕获,最佳写在颇具 JS
    脚本的前方,因为您无法保险你写的代码是否出错,假如写在背后,一旦产生错误的话是不会被
    onerror 捕获到的。
  2. 除此以外 onerror 是无能为力捕获到网络尤其的谬误。

当我们相见 <img src="./404.png">报 404 网络请求非凡的时候,onerror
是无能为力援救大家捕获到尤其的。

JavaScript

<script> window.onerror = function (msg, url, row, col, error) {
console.log(‘作者领悟异步错误了’); console.log({ msg, url, row, col, error
}) return true; }; </script> <img src=”./404.png”>

1
2
3
4
5
6
7
8
9
10
<script>
  window.onerror = function (msg, url, row, col, error) {
    console.log(‘我知道异步错误了’);
    console.log({
      msg,  url,  row, col, error
    })
    return true;
  };
</script>
<img src="./404.png">

澳门葡京 8

由于互连网请求格外不会事件冒泡,因而必须在捕获阶段将其捕捉到才行,可是这种措施纵然可以捕捉到网络请求的十二分,但是力不从心看清
HTTP 的事态是 404 仍旧任何诸如 500
等等,所以还索要同盟服务端日志才进行排查分析才足以。

JavaScript

<script> window.addEventListener(‘error’, (msg, url, row, col,
error) => { console.log(‘我知道 404 错误了’); console.log( msg, url,
row, col, error ); return true; }, true); </script> <img
src=”./404.png” alt=””>

1
2
3
4
5
6
7
8
9
10
<script>
window.addEventListener(‘error’, (msg, url, row, col, error) => {
  console.log(‘我知道 404 错误了’);
  console.log(
    msg, url, row, col, error
  );
  return true;
}, true);
</script>
<img src="./404.png" alt="">

澳门葡京 9

那一点知识也许供给了然,要不然用户访问网站,图片 CDN
不可能服务,图片加载不出来而开发人士没有察觉就狼狈了。

1、JS脚本里边存着语法错误;

① 、JS Error 监察和控制成效 (数据大概浏览)

澳门葡京 10

 

为了拿走那几个数据,大家必要在上传的时候将其分析出来。在许多日志分析中,很多字段及成效是重复通用的,所以理应将其包装起来。

// 设置日志对象类的通用属性
  function setCommonProperty() {
    this.happenTime = new Date().getTime(); // 日志发生时间
    this.webMonitorId = WEB_MONITOR_ID;     // 用于区分应用的唯一标识(一个项目对应一个)
    this.simpleUrl =  window.location.href.split('?')[0].replace('#', ''); // 页面的url
    this.customerKey = utils.getCustomerKey(); // 用于区分用户,所对应唯一的标识,清理本地数据后失效
    this.pageKey = utils.getPageKey();  // 用于区分页面,所对应唯一的标识,每个新页面对应一个值
    this.deviceName = DEVICE_INFO.deviceName;
    this.os = DEVICE_INFO.os + (DEVICE_INFO.osVersion ? " " + DEVICE_INFO.osVersion : "");
    this.browserName = DEVICE_INFO.browserName;
    this.browserVersion = DEVICE_INFO.browserVersion;
    // TODO 位置信息, 待处理
    this.monitorIp = "";  // 用户的IP地址
    this.country = "china";  // 用户所在国家
    this.province = "";  // 用户所在省份
    this.city = "";  // 用户所在城市
    // 用户自定义信息, 由开发者主动传入, 便于对线上进行准确定位
    this.userId = USER_INFO.userId;
    this.firstUserParam = USER_INFO.firstUserParam;
    this.secondUserParam = USER_INFO.secondUserParam;
  }

  // JS错误日志,继承于日志基类MonitorBaseInfo
  function JavaScriptErrorInfo(uploadType, errorMsg, errorStack) {
    setCommonProperty.apply(this);
    this.uploadType = uploadType;
    this.errorMessage = encodeURIComponent(errorMsg);
    this.errorStack = errorStack;
    this.browserInfo = BROWSER_INFO;
  }
  JavaScriptErrorInfo.prototype = new MonitorBaseInfo();

  封装了3个JsError对象JavaScriptErrorInfo,用以保存页面中爆发的Js错误。个中,setCommonProperty用以设置有着日志对象的通用属性。

  1)重写window.onerror 方法,
大家领会,监察和控制JS错误必然离不开它,有人对她进行了测试测试介绍深感也是相比用心了

  2)重写console.error方法,为啥要重写那几个方法,作者不能交给显然的答案,假使App第③回向浏览器注入的Js代码报错了,window.onerror是力不从心监督到的,所以只可以以此方式来开始展览捕获,大概会有更好的艺术,待window.onerror成功后,此方法便不再要求用了

  上面是开发银行JS错误监察和控制代码

/**
   * 页面JS错误监控
   */
  function recordJavaScriptError() {
    // 重写console.error, 可以捕获更全面的报错信息
    var oldError = console.error;
    console.error = function () {
      // arguments的长度为2时,才是error上报的时机
      // if (arguments.length < 2) return;
      var errorMsg = arguments[0] && arguments[0].message;
      var url = WEB_LOCATION;
      var lineNumber = 0;
      var columnNumber = 0;
      var errorObj = arguments[0] && arguments[0].stack;
      if (!errorObj) errorObj = arguments[0];
      // 如果onerror重写成功,就无需在这里进行上报了
      !jsMonitorStarted && siftAndMakeUpMessage(errorMsg, url, lineNumber, columnNumber, errorObj);
      return oldError.apply(console, arguments);
    };
    // 重写 onerror 进行jsError的监听
    window.onerror = function(errorMsg, url, lineNumber, columnNumber, errorObj)
    {
      jsMonitorStarted = true;
      var errorStack = errorObj ? errorObj.stack : null;
      siftAndMakeUpMessage(errorMsg, url, lineNumber, columnNumber, errorStack);
    };

    function siftAndMakeUpMessage(origin_errorMsg, origin_url, origin_lineNumber, origin_columnNumber, origin_errorObj) {
      var errorMsg = origin_errorMsg ? origin_errorMsg : '';
      var errorObj = origin_errorObj ? origin_errorObj : '';
      var errorType = "";
      if (errorMsg) {
        var errorStackStr = JSON.stringify(errorObj)
        errorType = errorStackStr.split(": ")[0].replace('"', "");
      }
      var javaScriptErrorInfo = new JavaScriptErrorInfo(JS_ERROR, errorType + ": " + errorMsg, errorObj);
      javaScriptErrorInfo.handleLogInfo(JS_ERROR, javaScriptErrorInfo);
    };
  };

OK,
错误日志有了,该怎么计算错误率呢?

  JS错误爆发率 =
JS错误个数(二回访问页面中,全体的js错误都算壹回)/PV
(PC,IOS,Android平台同理)

因此大家要求记下页面包车型地铁PV记录

 // 用户访问行为日志(PV)
    function CustomerPV(uploadType, loadType, loadTime) {
      setCommonProperty.apply(this);
      this.uploadType = uploadType;
      this.loadType = loadType;  // 用以区分首次加载
      this.loadTime = loadTime; // 加载时间
    }
    CustomerPV.prototype = new MonitorBaseInfo();
    /**
     * 添加一个定时器,进行数据的上传
     * 3秒钟进行一次URL是否变化的检测
     * 15秒钟进行一次数据的检查并上传
     */
    var defaultLocation = window.location.href.split('?')[0].replace('#', '');
    var timeCount = 0;
    setInterval(function () {
      // 如果是单页应用, 只更改url
      var webLocation = window.location.href.split('?')[0].replace('#', '');
      // 如果url变化了, 就把更新的url记录为 defaultLocation, 重新设置pageKey
      if (defaultLocation != webLocation) {
        recordPV();
        defaultLocation = webLocation;
      }
      // 循环5后次进行一次上传
      if (timeCount >= 5) {
        var logInfo = localStorage[ELE_BEHAVIOR] || "" +
          localStorage[JS_ERROR] || "" +
          localStorage[CUSTOMER_PV] || "";
        if (logInfo) {
          utils.ajax("POST", HTTP_UPLOAD_LOG_INFO, {logInfo: logInfo}, function (res) {
            // 上传完成后,清空本地记录
            if (res.code === 200) {
              localStorage[ELE_BEHAVIOR] = "";
              localStorage[JS_ERROR] = "";
              localStorage[CUSTOMER_PV] = "";
            }
          })
        }
        timeCount = 0;
      }
      timeCount ++;
    }, 3000);

  上边的代码作者用了定时器,大概的情致是3秒实行贰遍U中华VL变化的自笔者批评,15秒实行贰回数据的自笔者批评,如若有数据就实行上传,并清空上1回的数额。为何用定时器呢,因为在单页应用中,路由的切换和地址栏的浮动是不能被监察和控制的,作者确实没有想到特别好的主意来监督,所以用了那种措施,万一有人有更好的艺术,请给自己留言,多谢

 

onerror事件
/**
  * @param {String}  msg    错误信息
  * @param {String}  url    出错文件
  * @param {Number}  row    行号
  * @param {Number}  col    列号
  * @param {Object}  error  错误详细信息
  */
window.onerror = function (msg, url, row, col, error) {
  console.log('我知道错误了');
  console.log({
    msg,  url,  row, col, error
  })
  return true; // 注意,在返回 true 的时候,异常才不会继续向上抛出error;
};

打印如下:

我知道错误了
{
    msg: "Uncaught ReferenceError: error is not defined", 
    url: "file:///Users/beifeng/Desktop/test.html", 
    row: 25, 
    col: 5, 
    error: ReferenceError: error is not defined at 
}

通过为页面上的 script 标签添加 crossOrigin
属性完毕跨域上报,别忘了服务器也安装 Access-Control-Allow-Origin
的响应头。(消除跨域的js脚本错误上报)

不以为奇我们采纳window.onerror来捕获js脚本的错误新闻。可是对于跨域调用的js脚本,onerror事件只会付给很少的报错消息:error:
Script
error.那一个差不离的新闻很备受瞩目不足以看出脚本的切实错误,所以大家得以行使crossorigin属性,使得加载的跨域脚本能够得出跟同域脚本同样的报错音讯:
<script crossorigin src="http://www.lmj.com/demo/crossoriginAttribute/error.js"></script>
若果是这么,www.lmj.com的服务器必须提交一个Access-Control-Allow-Origin的header,不然不大概访问此脚本。

// 举个例子
// http://localhost:8080/index.html
<script>
  window.onerror = function (msg, url, row, col, error) {
    console.log('我知道错误了,也知道错误信息');
    console.log({
      msg,  url,  row, col, error
    })
    return true;
  };
</script>
<script src="http://localhost:8081/test.js" crossorigin></script>

// http://localhost:8081/test.js
setTimeout(() => {
  console.log(error);
});

onerror事件
是无能为力捕获到互联网越发的不当(财富加载战败,裸奔,图片彰显分外等)。当大家相遇
<img src="./404.png"> 报 404 互联网请求十分的时候,onerror
是无能为力帮忙我们捕获到万分的。

Promise 错误

通过 Promise 能够扶助大家缓解异步回调鬼世界的难题,然则一旦 Promise
实例抛出尤其而你未曾用 catch 去捕获的话,onerror 或 try-catch
也无力回天,不能够捕捉到错误。

JavaScript

window.add伊夫ntListener(‘error’, (msg, url, row, col, error) => {
console.log(‘笔者感知不到 promise 错误’); console.log( msg, url, row, col,
error ); }, true); Promise.reject(‘promise error’); new
Promise((resolve, reject) => { reject(‘promise error’); }); new
Promise((resolve) => { resolve(); }).then(() => { throw ‘promise
error’ });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
window.addEventListener(‘error’, (msg, url, row, col, error) => {
  console.log(‘我感知不到 promise 错误’);
  console.log(
    msg, url, row, col, error
  );
}, true);
Promise.reject(‘promise error’);
new Promise((resolve, reject) => {
  reject(‘promise error’);
});
new Promise((resolve) => {
  resolve();
}).then(() => {
  throw ‘promise error’
});

澳门葡京 11

固然在写 Promise 实例的时候养成最终写上 catch
函数是个好习惯,不过代码写多了就不难糊涂,忘记写 catch。

故而只要您的选取用到很多的 Promise 实例的话,特别是你在局地基于 promise
的异步库比如 axios
等一定要小心,因为你不亮堂什么样时候那个异步请求会抛出十三分而你并从未拍卖它,所以你最棒添加三个Promise 全局10分捕获事件 unhandledrejection。

JavaScript

window.add伊芙ntListener(“unhandledrejection”, function(e){
e.preventDefault() console.log(‘笔者领会 promise 的不当了’);
console.log(e.reason); return true; }); Promise.reject(‘promise error’);
new Promise((resolve, reject) => { reject(‘promise error’); }); new
Promise((resolve) => { resolve(); }).then(() => { throw ‘promise
error’ });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
window.addEventListener("unhandledrejection", function(e){
  e.preventDefault()
  console.log(‘我知道 promise 的错误了’);
  console.log(e.reason);
  return true;
});
Promise.reject(‘promise error’);
new Promise((resolve, reject) => {
  reject(‘promise error’);
});
new Promise((resolve) => {
  resolve();
}).then(() => {
  throw ‘promise error’
});

澳门葡京 12

当然,如果你的应用尚未做 Promise
全局11分处理的话,那很或然就像是某乎首页这样:

澳门葡京 13

② 、JS脚本在运作时产生错误。

贰 、JS Error 详细音讯解析

 

澳门葡京 14  

  总结JS
Error的指标,一是为了精晓线上种类的健康处境,二是为着分析错误,协理大家探寻难点之四海,并且化解它。所以,为了寻找并消除难点,我们须要对多少个关键点实行解析。

  ① 
某种错误发生的次数——产生次数跟影响用户是成正比的,
即便爆发次数跟影响用户数量都很高,那么那是四个比较严重的bug,
要求即刻消除。 反之,
假诺次数过多,影响用户数量很少。表达那种错误只爆发在为数不多设备中,优先级相对较低,能够择时对该类机型配备举办包容处理。当然,ip地址访问次数也能证实那几个题材

  ② 
页面发生了怎么不当——那个有利大家缩小意思的范围,方便大家排查,如:

  澳门葡京 15

  ③ 
错误堆栈——那点并非说,是固定错误最首要的成分。平常意况下,代码都以被削减的,所以自个儿在后台解析并截取出错代码附近的一有的代码,进行体现,排查错误。 澳门葡京 16

  ④ 
设备音讯——当错误爆发是,分析出用户及时选用设备的浏览器信息,系统版本,设备机型等等,能够帮大家飞速的原则性到需求合作的设备,进而进步化解难点的频率。

  ⑤  用户足迹——笔者个人认为比较有用,不过代价太高。
因为这些须求记录下用户在页面上的全部行为,需求上传分外多的数据,功用待定。

  澳门葡京 17

  到此,已经采集到了JS错误日志的大部音信了,并且一度分析出JS错误的详细音讯了。只必要将其上传,入库,再拓展解析突显,就足以旁观JS错误消息的预览效果和详情。所以,大家再去布置一下后台代码。

 

  上一章:
搭建前端监察和控制系统(一)Ali云服务器搭建篇

  下一章:
搭建前端监察和控制种类(三)NodeJs服务器铺排篇

 


 

  

  为了将这几个多少上传到大家的服务器,大家总不可能每一遍都用xmlHttpRequest来发送ajax请求吧,

  所以大家必要本身包裹四个归纳的Ajax

/**
     *
     * @param method  请求类型(大写)  GET/POST
     * @param url     请求URL
     * @param param   请求参数
     * @param successCallback  成功回调方法
     * @param failCallback   失败回调方法
     */
    this.ajax = function(method, url, param, successCallback, failCallback) {
      var xmlHttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
      xmlHttp.open(method, url, true);
      xmlHttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
      xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
          var res = JSON.parse(xmlHttp.responseText);
          typeof successCallback == 'function' && successCallback(res);
        } else {
          typeof failCallback == 'function' && failCallback();
        }
      };
      xmlHttp.send("data=" + JSON.stringify(param));
    }

 

window.addEventListener监听error事件

是因为网络请求格外不会事件冒泡,由此必须在抓获阶段将其捕捉到才行,可是那种艺术固然可以捕捉到网络请求的万分,不过无法判定
HTTP 的情形是 404 照旧别的诸如 500
等等,所以还亟需般配服务端日志才实行排查分析才得以。

<script>
/**
  * @param {String}    event         监听事件
  * @param {function}  function      出错文件
  * @param {Boolean}   useCapture    指定事件是否在捕获或冒泡阶段执行。
  *        true - 事件句柄在捕获阶段执行
  *        false- false- 默认。事件句柄在冒泡阶段执行
  */
window.addEventListener('error', (error) => {
  console.log('我知道 404 错误了');
  console.log(
    error
  );
  return true;
}, true);
</script>
<img src="./404.png" alt="澳门葡京 18">


打印如下:

我知道 404 错误了
<!--资源加载错误-->
bubbles:false
cancelBubble:false
cancelable:false
composed:false
currentTarget:null
defaultPrevented:false
falseeventPhase:0
isTrusted:true
path:Array(5) // dom树
  0:img
  1:div
  2:body
  3:html
  4:document
  5:Window
returnValue:true
srcElement:img // 发生错误的dom
target:img
timeStamp:5.545000000000001 // 错误发生的时间(按页面加载时间为0)
type:"error"

<!--运行时错误-->
bubbles:false
cancelBubble:false
cancelable:true
colno:13 // 列号
composed:false
currentTarget:Window
defaultPrevented:true
error:TypeError: hahahah at file:///home/jhjr/Desktop/%E5%89%8D%E7%AB%AF%E5%BC%82%E5%B8%B8%E7%9B%91%E6%8E%A7/test.html:22:13
  message:"Unexpected identifier" // 错误信息
  stack:"SyntaxError: Unexpected identifier" // 错误栈
eventPhase:0
filename:"file:///home/jhjr/Desktop/%E5%89%8D%E7%AB%AF%E5%BC%82%E5%B8%B8%E7%9B%91%E6%8E%A7/test.html" // 错误文件
isTrusted:true
lineno:22 // 行号
message:"Uncaught TypeError: hahahah"
path:Array(1) // DOM树
returnValue:false
srcElement:Window // 发生错误的dom
target:Window
timeStamp:1005.4350000000001 // 错误发生的时间(按页面加载时间为0)
type:"error"

网上说add伊夫ntListener的浏览器包容性不太好,去Can I
use查了一晃其实幸而.具体看那里addEventListener.

还有3个难题是它的error对象在区别浏览器会有不一致的七个反映,那里必要小心下.

不行申报格局

监察获得报错新闻之后,接下去就要求将捕捉到的错误音讯发送到新闻搜集平台上,常用的发送方式主要有三种:

  1. 由此 Ajax 发送数据
  2. 动态创设 img 标签的花样
JavaScript

function report(error) { var reportUrl = 'http://xxxx/report'; new
Image().src = reportUrl + 'error=' + error; }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5a707ba98744f433416112-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba98744f433416112-2">
2
</div>
<div class="crayon-num" data-line="crayon-5a707ba98744f433416112-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba98744f433416112-4">
4
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5a707ba98744f433416112-1" class="crayon-line">
function report(error) {
</div>
<div id="crayon-5a707ba98744f433416112-2" class="crayon-line crayon-striped-line">
  var reportUrl = 'http://xxxx/report';
</div>
<div id="crayon-5a707ba98744f433416112-3" class="crayon-line">
  new Image().src = reportUrl + 'error=' + error;
</div>
<div id="crayon-5a707ba98744f433416112-4" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

实例 – 动态创造 img 标签举办反馈

接近于那种:

promise极度捕获

当代的浏览器其实早就能够帮忙promise语法了,所以在promise非常捕获这一块大家也依然要小心一下.

  • 人工手动catch捕获(那么些是基本的,和try…catch…是如出一辙的).
  • 经过浏览器自带的unhandledrejection事件来监听全局没有catch的promise执行.可是那一个的包容性不是很好,具体能够看下unhandledrejection

<script>
  window.addEventListener('unhandledrejection', function(err) {
    console.log(err);
  });
</script>

new Promise(function(resolve, reject) {
  reject(new Error('haha'))
})

打印如下:

bubbles:false
cancelBubble:false
cancelable:true
composed:false
currentTarget:Window
defaultPrevented:false
eventPhase:0
isTrusted:true
path:Array(1)
promise:Promise // 捕获到的错误promise
reason:Error: haha at http://localhost:3000/promise_error:21:12 at Promise  (<anonymous>) at http://localhost:3000/promise_error:20:3  // 其实就是错误栈
  message: "haha"
  stack: "Error: haha↵    at http://localhost:3000/promise_error:21:12↵    at Promise (<anonymous>)↵    at http://localhost:3000/promise_error:20:3"
returnValue:true
srcElement:Window
target:Window
timeStamp:55.190000000000005
type:"unhandledrejection"

监督检查上报常见难题

下述例子作者全方位位于小编的 github 上,读者可以自动查阅,前面不再赘述。

JavaScript

git clone cd blog/code/jserror/
npm install

1
2
3
git clone https://github.com/happylindz/blog.git
cd blog/code/jserror/
npm install
for(var i=0;i<l;i++){
 console.log(i);
}

十三分怎么着反馈

监察和控制得到报错消息之后,接下去就需求将捕捉到的错误消息发送到音信搜集平台上,常用的出殡和埋葬格局首要有二种:

  • 因此 Ajax 发送数据(xhr和jquery)
  • 动态创造 img 标签的款式

    function report(error) {
      var reportUrl = 'http://xxxx/report';
      new Image().src = reportUrl + 'error=' + error;
    }
    

末尾挑选的要么经过xhr的方法,考虑不想依靠别的script文件。

Script error 脚本错误是什么样

因为大家在线上的版本,平时做静态能源 CDN
化,那就会造成大家常访问的页面跟脚本文件来自差异的域名,那时候借使没有进展额外的配置,就会不难发生Script error。

澳门葡京 19

可通过 npm run nocors 查看效果。

Script error
是浏览器在同源策略限制下发生的,浏览器处于对安全性上的考虑,当页面引用非同域名外部脚本文件时中抛出特别的话,此时本页面是从未义务知道那么些报错新闻的,取而代之的是出口
Script error 那样的消息。

澳门葡京 20

那样做的目标是制止数据外泄到不安全的域中,举个大致的例证,

JavaScript

<script src=”xxxx.com/login.html”></script>

1
<script src="xxxx.com/login.html"></script>

地方大家并从未引入一个 js 文件,而是3个 html,这一个 html
是银行的记名页面,借使您早就报到了,那 login 页面就会自行跳转到
Welcome xxx...,假设未登录则跳转到 Please Login...,那么报错也会是
Welcome xxx... is not defined,Please Login... is not defined,通过这几个音信能够断定二个用户是不是登录他的帐号,给入侵者提供了万分有利的论断渠道,那是13分不安全的。

介绍完背景后,那么我们应当去化解那一个标题?

第叁能够想到的方案肯定是同源化策略,将 JS 文件内联到 html
也许放置同域下,就算能简单有效地缓解 script error
难题,可是如此无法运用好文件缓存和 CDN
的优势,不引进应用。正确的法子应该是从根本上缓解 script error 的失实。

澳门葡京 21

数据结构

var deviceInfo = {
  "c": "website", // 客户端类别
  "p": "web-mobile-pay", // 客户端包名
  "l": "error", // 日志级别
  "t": new Date().getTime(), // 事件发生时间
  "v": Logger.version, // 客户端版本号
  "uid": userId,
  "ua": navigator.userAgent
};
var logs = [{
  "tag": data.tag || window.location.pathname, // tag 默认为网页路由
  "message": data.message || error.message || '',
  "url": window.location.href,  // 网址
  "filename": data.filename || "",  // 若全局捕获,文件名
  "lineno": data.lineno || 0, // 若全局捕获,行号
  "colno": data.colno || 0, // 若全局捕获,列号
  "domPath": domPath, // 若全局捕获页面dom问题,dom路径
  "element": element.outerHTML || "", // 若全局捕获页面dom问题,出错html代码
  "error": {
    "name": error.name || "",
    "message": error.message || "",
    "stack": error.stack || ""
  },
  uid: userId,
  userName: userName,
  mobile: mobile,
  branchNo: branchNo,
  idNo: idNo,
  clientId: clientId,
  clientName: clientName
}];

{deviceInfo: deviceInfo, logs: logs}

那边参照了大家已部分日志服务的着力数据格式和平运动动网站的日记上报有关的字段.
重庆大学取得的新闻包括:设备音讯, 浏览器新闻, 用户音讯, 错误音信.

跨源能源共享机制( CO奥迪Q3S )

率先为页面上的 script 标签添加 crossOrigin 属性

JavaScript

// <script> window.onerror =
function (msg, url, row, col, error) {
console.log(‘笔者领悟不当了,也晓得不当信息’); console.log({ msg, url,
row, col, error }) return true; }; </script> <script
src=”” crossorigin></script> //
setTimeout(() => { console.log(error);
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// http://localhost:8080/index.html
<script>
  window.onerror = function (msg, url, row, col, error) {
    console.log(‘我知道错误了,也知道错误信息’);
    console.log({
      msg,  url,  row, col, error
    })
    return true;
  };
</script>
<script src="http://localhost:8081/test.js" crossorigin></script>
 
// http://localhost:8081/test.js
setTimeout(() => {
  console.log(error);
})

当您改改完前端代码后,你还索要额外给后端在响应头里加上
Access-Control-Allow-Origin: localhost:8080,那里自个儿以 Koa 为例。

JavaScript

const Koa = require(‘koa’); const path = require(‘path’); const cors =
require(‘koa-cors’); const app = new Koa(); app.use(cors());
app.use(require(‘koa-static’)(path.resolve(__dirname, ‘./public’)));
app.listen(8081, () => { console.log(‘koa app listening at 8081’) });

1
2
3
4
5
6
7
8
9
10
11
const Koa = require(‘koa’);
const path = require(‘path’);
const cors = require(‘koa-cors’);
const app = new Koa();
 
app.use(cors());
app.use(require(‘koa-static’)(path.resolve(__dirname, ‘./public’)));
 
app.listen(8081, () => {
  console.log(‘koa app listening at 8081’)
});

澳门葡京 22

读者可透过 npm run cors澳门葡京,
详细的跨域知识笔者就不开始展览了,有趣味可以看看自家事先写的篇章:跨域,你需求精通的全在此间

您认为这么就完了吗?并不曾,上边就说有的 Script error 你不常遇见的点:

大家都通晓 JSONP
是用来跨域获取数据的,并且兼容性非凡,在局地选取中依然会采取到,所以你的门类中大概会用那样的代码:

JavaScript

// window.onerror = function (msg, url,
row, col, error) { console.log(‘小编精通不当了,但不明了不当音讯’);
console.log({ msg, url, row, col, error }) return true; }; function
jsonpCallback(data) { console.log(data); } const url =
”; const script =
document.createElement(‘script’); script.src = url;
document.body.appendChild(script);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// http://localhost:8080/index.html
window.onerror = function (msg, url, row, col, error) {
  console.log(‘我知道错误了,但不知道错误信息’);
  console.log({
    msg,  url,  row, col, error
  })
  return true;
};
function jsonpCallback(data) {
  console.log(data);
}
const url = ‘http://localhost:8081/data?callback=jsonpCallback’;
const script = document.createElement(‘script’);
script.src = url;
document.body.appendChild(script);

因为重返的音信会当做脚本文件来进行,一旦回到的剧本内容出错了,也是力不从心捕捉到错误的消息。

澳门葡京 23

化解办法也简单,跟以前同一,在抬高动态拉长脚本的时候添加
crossOrigin,并且在后端配上相应的 CO瑞虎S 字段即可.

JavaScript

const script = document.createElement(‘script’); script.crossOrigin =
‘anonymous’; script.src = url; document.body.appendChild(script);

1
2
3
4
const script = document.createElement(‘script’);
script.crossOrigin = ‘anonymous’;
script.src = url;
document.body.appendChild(script);

读者能够通过 npm run jsonp 查看效果

澳门葡京 24

精晓原理之后你大概会认为没什么,不就是给各个动态变化的剧本添加
crossOrigin 字段嘛,可是在骨子里工程中,你大概是面向广大库来编程,比如利用
jQuery,Seajs 也许 webpack
来异步加载脚本,许多库封装了异步加载脚本的力量,以 jQeury
为例你大概是那样来触发异步脚本。

JavaScript

$.ajax({ url: ”, dataType: ‘jsonp’, success:
(data) => { console.log(data); } })

1
2
3
4
5
6
7
$.ajax({
  url: ‘http://localhost:8081/data’,
  dataType: ‘jsonp’,
  success: (data) => {
    console.log(data);
  }
})

借使这一个库中没有提供 crossOrigin 的能力的话(jQuery jsonp
只怕有,假装你不驾驭),那你只可以去修改人家写的源代码了,所以我那边提供一个思路,便是去劫持document.createElement,从根源上去为各类动态变化的本子添加 crossOrigin
字段。

JavaScript

document.createElement = (function() { const fn =
document.createElement.bind(document); return function(type) { const
result = fn(type); if(type === ‘script’) { result.crossOrigin =
‘anonymous’; } return result; } })(); window.onerror = function (msg,
url, row, col, error) { console.log(‘笔者驾驭不当了,也亮堂不当消息’);
console.log({ msg, url, row, col, error }) return true; }; $.ajax({ url:
”, dataType: ‘jsonp’, success: (data) => {
console.log(data); } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
document.createElement = (function() {
  const fn = document.createElement.bind(document);
  return function(type) {
    const result = fn(type);
    if(type === ‘script’) {
      result.crossOrigin = ‘anonymous’;
    }
    return result;
  }
})();
window.onerror = function (msg, url, row, col, error) {
  console.log(‘我知道错误了,也知道错误信息’);
  console.log({
    msg,  url,  row, col, error
  })
  return true;
};
$.ajax({
  url: ‘http://localhost:8081/data’,
  dataType: ‘jsonp’,
  success: (data) => {
    console.log(data);
  }
})

功用也是如出一辙的,读者能够由此 npm run jsonpjq 来查看效果:

澳门葡京 25

诸如此类重写 createElement
理论上没什么难点,但是入侵了原来的代码,不有限支撑一定不会出错,在工程上照旧须求多尝试下看看再选择,恐怕存在包容性上难题,假若您认为会并发什么难点的话也欢迎留言商量下。

有关 Script error
的难题就写到那里,即使你领悟了地点的内容,基本上绝大多数的 Script error
都能缓解。

那么大家怎么样来捕获那种很是呢,有两种格局,

何以平滑的运用在作业连串中

window.onerror 能还是不能够捕获 iframe 的谬误

当您的页面有选取 iframe 的时候,你必要对你引入的 iframe
做充裕监控的处理,不然一经您引入的 iframe
页面出现了难题,你的主站展现不出去,而你却雾里看花。

第2须求强调,父窗口直接动用 window.onerror
是无力回天间接破获,假使你想要捕获 iframe 的尤其的话,有分好两种情形。

一旦你的 iframe 页面和你的主站是同域名的话,直接给 iframe 添加 onerror
事件即可。

JavaScript

<iframe src=”./iframe.html” frameborder=”0″></iframe>
<script> window.frames[0].onerror = function (msg, url, row,
col, error) { console.log(‘作者清楚 iframe 的荒谬了,也清楚不当消息’);
console.log({ msg, url, row, col, error }) return true; };
</script>

1
2
3
4
5
6
7
8
9
10
<iframe src="./iframe.html" frameborder="0"></iframe>
<script>
  window.frames[0].onerror = function (msg, url, row, col, error) {
    console.log(‘我知道 iframe 的错误了,也知道错误信息’);
    console.log({
      msg,  url,  row, col, error
    })
    return true;
  };
</script>

读者能够由此 npm run iframe 查看效果:

澳门葡京 26

一旦您置于的 iframe 页面和您的主站不是同个域名的,不过 iframe
内容不属于第③方,是您可以操纵的,那么能够因此与 iframe
通讯的不二法门将十三分消息抛给主站接收。与 iframe
通讯的格局有很多,常用的如:postMessage,hash 或许 name
字段跨域等等,那里就不实行了,感兴趣的话能够看:跨域,你需求明白的全在此地

比方是非同域且网站不受本身决定以来,除了通过控制台看到详细的错误音讯外,不能捕获,那是由于安全性的考虑,你引入了3个百度首页,人家页面报出的错误凭啥让你去监督呢,那会引出很多安全性的难点。

首先种是try..catch

自动化添加监听文件

通过gulp的插件gulp-inject招来ejs/html文件中的标签<head><title>拓展插队对应的script脚本。

gulp.task('inject-js', function () {
  return gulp.src('src/views/**/*.ejs')
    .pipe(inject(gulp.src(['./src/public/js/*.js']), {
      starttag: '<head>',
      endtag: '<title>'
    }))
    .pipe(gulp.dest('dist/test'));
});

削减代码如何定位到脚本格外地点

线上的代码大概都经过了滑坡处理,几十二个文本打包成了三个并丑化代码,当我们吸收
a is not defined 的时候,大家平素不通晓那个变量 a
毕竟是怎么意思,此时报错的错误日志显著是不行的。

首先想到的章程是运用 sourcemap
定位到错误代码的具体地方,详细内容能够参见:Sourcemap
定位脚本错误

其它也能够经过在卷入的时候,在各样合并的文书之间添加几行空格,并相应增加有的表明,那样在定位难题的时候很不难能够领悟是哪位文件报的失实,然后再通过一些人命关天词的检索,能够飞快地稳定到题指标所在地方。

第二种是 window.onerror

制止add伊芙ntListener重复监听

  1. 通过在window对象上添加3个字段errorListenerStatus来作为error监听的标识。
  2. 尽量防止在非主文件上去注入script脚本。

采集非凡音讯量太多,怎么做

假设你的网站访问量相当的大,借使网页的 PV 有
1kw,那么1个毫无疑问的一无所长发送的音信就有 1kw
条,我们能够给网站设置七个采集率:

JavaScript

Reporter.send = function(data) { // 只采集 百分之三十 if(Math.random() <
0.3) { send(data) // 上报错误消息 } }

1
2
3
4
5
6
Reporter.send = function(data) {
  // 只采集 30%
  if(Math.random() < 0.3) {
    send(data)      // 上报错误信息
  }
}

其一采集率能够经超过实际际实在的动静来设定,方法多种化,能够动用两个随意数,也足以切切实实遵照用户的少数特征来展开判断。

地方大致是自小编对前者代码监控的一对明亮,说起来简单,可是若是在工程化应用,难免须求考虑到包容性等样样难点,读者能够经过投机的具体意况进行调整,前端代码相当监察和控制对于大家的网站的稳定起着至关心爱护要的作用。如果文中有着不对的地方,还望指正。

由于try.catch 无法捕捉到全局的失实事件,也便是说
唯有try,catch的块里边运转出错才会被你捕捉到。所以我们那边排除它的那种方案,

任何题材

  • 减少代码不恐怕稳定到不当具体地方如何是好
    sourceMap解析

  • 用户作为记录

    • 能够在cookie里记录用户在网站的拜访记录
    • 对不当发生页面实行页面截屏

    不是很好的主意,当时技能上是足以兑现的。

参照小说

  • 脚本错误量极致优化-监察和控制上报与Script
    error
  • 前端代码卓殊日志收集与监督
  • 前者魔法堂——非常不仅仅是try/catch

    1 赞 2 收藏
    评论

来选择第壹种情势,也正是window.onerror方法。

具体代码

var Logger = {
  maxRouterNum: 5,
  getCookie: function (name) {
    var arr;
    var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
    if ((arr = document.cookie.match(reg))) return decodeURIComponent((arr[2]));
    else return '';
  },
  dataStructChange: function (data) {
    var element = data.srcElement || data.target || {};
    var error = data.reason || data.error || {};
    var domPath = '';
    var router = Logger.getRouter();
    // cookie 解析
    var cookies = document.cookie ? document.cookie.split('; ').reduce(function (total, currentValue, currentIndex, arr) {
      var cookieArr = currentValue.split('=');
      total[cookieArr[0]] = cookieArr[1];
      return total;
    }, {}) : {};

    data.path && (domPath = data.path.map(function (item) {
      return item.nodeName || 'window';
    }).join(', '));

    var p = Logger.getCookie('package');
    var version = Logger.getCookie('version');
    var userId = Logger.getCookie('userId');

    // 基本数据结构
    var deviceInfo = {
      'c': 'website', // 客户端类别
      'p': p, // 客户端包名
      'l': 'error', // 日志级别
      't': new Date().getTime(), // 事件发生时间
      'v': version, // 客户端版本号
      'uid': userId,
      'ua': navigator.userAgent
    };
    var logs = [{
      'tag': data.tag || window.location.pathname, // tag 默认为网页路由
      'message': data.message || error.message || '',
      'url': window.location.href, // 网址
      'filename': data.filename || '', // 若全局捕获,文件名
      'lineno': data.lineno || 0, // 若全局捕获,行号
      'colno': data.colno || 0, // 若全局捕获,列号
      'domPath': domPath, // 若全局捕获页面dom问题,dom路径
      'element': element.outerHTML || '', // 若全局捕获页面dom问题,出错html代码
      'error': {
        'name': error.name || '',
        'message': error.message || '',
        'stack': error.stack || ''
      },
      'router': router, // 用户访问路径
      'cookies': cookies
    }];

    // console.log({deviceInfo: deviceInfo, logs: logs});

    return 'deviceInfo=' + JSON.stringify(deviceInfo) + '&logs=' + JSON.stringify(logs);
  },
  // 请求
  request: function (data, url) {
    try {
      // gulp-replace
      var requestUrl = Logger.getCookie('env') == 'production' ? 'production' : 'staging';
      url = url || requestUrl;
      // gulp-replace end

      // 创建异步对象
      var xhr = new XMLHttpRequest();
      // 设置请求的类型及url
      xhr.open('post', url, true);
      // post请求一定要添加请求头才行不然会报错
      xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
      // 发送请求,此调用可能失败catch
      xhr.send(data);
      xhr.onreadystatechange = function () {
        // 这步为判断服务器是否正确响应
        if (xhr.readyState == 4 && xhr.status == 200) {
          // console.log(xhr.responseText);
        }
      };
    } catch (e) {
      console.log(e);
    }
  },
  errorReport: function (data) {
    Logger.request(Logger.dataStructChange(data));
  },
  error: function (error, tag, message) {
    Logger.errorReport({tag: tag, error: error, message: message});
  },
  setRouter: function () {
    var maxNum = Logger.maxRouterNum;
    var router = sessionStorage.getItem('router');
    var routerArr = [];

    if (router) {
      // 每个记录之间用|分割
      routerArr = router.split('|');
      // 只记录最近的5个访问
      routerArr.length >= maxNum && (routerArr = routerArr.slice(routerArr.length - maxNum + 1));
    }

    routerArr.push(JSON.stringify({path: window.location.href, date: new Date().getTime()}));
    sessionStorage.setItem('router', routerArr.join('|'));
  },
  getRouter: function () {
    var router = sessionStorage.getItem('router');
    return router || '';
  }
};

if (!window.errorListenerStatus) { // 避免多次监听
  if (window.addEventListener) { // 所有主流浏览器,除了 IE 8 及更早版本
    // 全局error监听
    window.addEventListener('error', Logger.errorReport, true);
  } else if (window.attachEvent) { // IE 8 及更早版本
    window.attachEvent('onerror', Logger.errorReport);
  }

  // 全局promise no catch error监听
  // 支持性不太好,火狐不支持
  window.addEventListener('unhandledrejection', Logger.errorReport, true);
  window.errorListenerStatus = true;
}

// 用户访问记录
Logger.setRouter();

// 设备信息
// 手机型号, 手机系统

// 浏览器信息
// 浏览器类型

// 错误信息
// 页面路径, 文件名称, 错误行列号, 错误类型, 错误信息, 错误栈(DOM树), 时间戳,

// 用户信息
// cookie

window.onerror

传送门

前者监察和控制相当方案
如何做前端卓殊监控?
前者代码相当监察和控制

前者十分捕获方法
前端魔法堂——万分不仅仅是try/catch
Capture and report JavaScript errors with
window.onerror
GlobalEventHandlers.onerror
add伊夫ntListener()
方法,事件监听

平整应用
gulp-inject

JavaScript Source Map
JavaScript Source Map
详解
运用source-map完结对已调整和裁减公布的前端代码的十一分捕获与记录
source-map

Raven.js

浏览器截屏
html2canvas
导出HTML5
Canvas图片,并上传服务器

新闻得到
js获取ip地址,操作系统,浏览器版本等新闻,可协作

开拓浏览器自带的开发者工具,当二个荒唐发生时,我们能够立刻赢得晋升,并且领会不当发生的职分以及调用的库房消息。

我们得以由此 window.onerror
来捕获页面上的各个本子执行分外,它能扶助大家获得实惠的音信。可是那些形式存在包容性难题,在分歧的浏览器上提供的数额不完全一致,

有个别过时的浏览器只好提供一些数据。它的用法如下:

window.onerror = function (message, url, lineNo, columnNo, error)

七个参数的意义如下:

一 、message {String}
错误音信。直观的一无所长描述音讯,不过有时候你真的无法从那在那之中来看端倪,特别是削减后脚本的报错音讯,大概让您越发纳闷。

② 、url {String}
产生错误对应的剧本路径,比如是您的报错了大概报错了。

三 、lineNo {Number} 错误爆发的行号。

肆 、columnNo {Number} 错误发生的列号。

伍 、error {Object} 具体的 error
对象,包括特别详实的一无可取调用堆栈消息,那对于固定错误格外有援救。

包容性难题

今非昔比浏览器对同八个错误的 message
是分歧等的。

IE10以下浏览器只好取获得 message,url 和
lineNo那八个参数,获取不到columnNo 和 error

不过 window.event 对象提供了
errorLineerrorCharacter,以此来对号入座相应的队列号消息。

在使用onerror的时候,我们能够使用arguments.callee.caller
来递归出调用堆栈,这一类音讯是最直接的错误音信音信,所以是须要求捕获并报告的。后边大家会用js去示范。

今非昔比浏览器暗中同意可获取的参数值:

澳门葡京 27

写1个js报错的上报库

既是知道了window.onerror的用法,为什么大家不来写四个js库来监督大家的前端js,废话少说,写之。

落到实处思路:

一 、收集window.onerror的八个参数

贰 、除了那四个参数,能够追加自定义参数

三 、发送到后台服务器

咱们权且给大家的库起名为 badJsReport

原理相比简单,代码如下:

/**
 * Name: badJsReport.js
 * Version 1.1.0
 * Author xianyulaodi
 * Address: https://github.com/xianyulaodi/badJsReport
 * Released on: December 22, 2016
 */
;(function(){
 'use strict';
 if (window.badJsReport){ 
 return window.badJsReport 
 };
 /*
 * 默认上报的错误信息
 */ 
 var defaults = {
 msg:'', //错误的具体信息
 url:'', //错误所在的url
 line:'', //错误所在的行
 col:'', //错误所在的列
 error:'', //具体的error对象
 };
 /*
 *ajax封装
 */
 function ajax(options) {
 options = options || {};
 options.type = (options.type || "GET").toUpperCase();
 options.dataType = options.dataType || "json";
 var params = formatParams(options.data);
 if (window.XMLHttpRequest) {
 var xhr = new XMLHttpRequest();
 } else { 
 var xhr = new ActiveXObject('Microsoft.XMLHTTP');
 }
 xhr.onreadystatechange = function () {
 if (xhr.readyState == 4) {
 var status = xhr.status;
 if (status >= 200 && status < 300) {
  options.success && options.success(xhr.responseText, xhr.responseXML);
 } else {
  options.fail && options.fail(status);
 }
 }
 }
 if (options.type == "GET") {
 xhr.open("GET", options.url + "?" + params, true);
 xhr.send(null);
 } else if (options.type == "POST") {
 xhr.open("POST", options.url, true);
 //设置表单提交时的内容类型
 xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 xhr.send(params);
 }
 }
 /*
 *格式化参数
 */
 function formatParams(data) {
 var arr = [];
 for (var name in data) {
 arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
 }
 arr.push(("v=" + Math.random()).replace(".",""));
 return arr.join("&");
 }
 /*
 * 合并对象,将配置的参数也一并上报
 */
 function cloneObj(oldObj) { //复制对象方法
 if (typeof(oldObj) != 'object') return oldObj;
 if (oldObj == null) return oldObj;
 var newObj = new Object();
 for (var prop in oldObj)
 newObj[prop] = oldObj[prop];
 return newObj;
 };
 function extendObj() { //扩展对象
 var args = arguments;
 if (args.length < 2) {return;}
 var temp = cloneObj(args[0]); //调用复制对象方法
 for (var n = 1,len=args.length; n <len; n++){
 for (var index in args[n]) {
 temp[index] = args[n][index];
 }
 }
 return temp;
 }
 /**
 * 核心代码区
 **/
 var badJsReport=function(params){
 if(!params.url){return}
 window.onerror = function(msg,url,line,col,error){
 //采用异步的方式,避免阻塞
 setTimeout(function(){
 //不一定所有浏览器都支持col参数,如果不支持就用window.event来兼容
 col = col || (window.event && window.event.errorCharacter) || 0;
 defaults.url = url;
 defaults.line = line;
 defaults.col = col;
 if (error && error.stack){
  //如果浏览器有堆栈信息,直接使用
  defaults.msg = error.stack.toString();
 }else if (arguments.callee){
  //尝试通过callee拿堆栈信息
  var ext = [];
  var fn = arguments.callee.caller;
  var floor = 3; //这里只拿三层堆栈信息
  while (fn && (--floor>0)) {
  ext.push(fn.toString());
  if (fn === fn.caller) {
  break;//如果有环
  }
  fn = fn.caller;
  }
  ext = ext.join(",");
  defaults.msg = error.stack.toString();
 }
 // 合并上报的数据,包括默认上报的数据和自定义上报的数据
 var reportData=extendObj(params.data || {},defaults);
 // 把错误信息发送给后台
 ajax({
  url: params.url, //请求地址
  type: "POST", //请求方式
  data: reportData, //请求参数
  dataType: "json",
  success: function (response, xml) {
  // 此处放成功后执行的代码
params.successCallBack&&params.successCallBack(response, xml);
  },
  fail: function (status) {
  // 此处放失败后执行的代码
  params.failCallBack&&params.failCallBack(status);
  }
  });
 },0);
 return true; //错误不会console浏览器上,如需要,可将这样注释
 };
 }
 window.badJsReport=badJsReport;
})();
/*===========================
badJsReport AMD Export
===========================*/
if (typeof(module) !== 'undefined'){
 module.exports = window.badJsReport;
}
else if (typeof define === 'function' && define.amd) {
 define([], function () {
 'use strict';
 return window.badJsReport;
 });
}

作者们封装了原生ajax,还有将报告的参数对象合并。并表露了3个大局方法
badJsReport

应用方法:

壹 、将badJsReport.js加载到任何的js以前

二 、简单的行使格局:(这几个执行格局要放在其他代码执行此前)

badJsReport({
 url:'http://www.baidu.com', //发送到后台的url *必须
})

③ 、假设供给新增上报参数,恐怕要通晓发送给后台的回调。能够用上面包车型大巴法门

badJsReport({
 url:'http://www.baidu.com', //发送到后台的url *必须
 data:{}, //自定义添加上报参数,比如app版本,浏览器版本 -可省略
 successCallBack:function(response, xml){
 // 发送给后台成功的回调,-可省略
 },
 failCallBack:function(error){
 // 发送给后台失败的回调,-可省略
 }
})

注意点:

① 、对于跨域的JS财富,window.onerror拿不到详细的新闻,供给往能源的伸手添加额外的头顶。

静态能源请求必要加多一个Access-Control-Allow-Origin底部,也正是内需后台加二个Access-Control-Allow-Origin,同时script引入外链的竹签供给加多三个crossorigin的习性。那样就能够收获精确的失误新闻。

② 、因为代码的结尾return
true,所以只要有错误音讯,浏览器不会console出来,如若要求浏览器console,能够注释掉最后的return
true

缺点:

对于减弱之后的代码,大家收获错误的音讯,可是大家却不可能稳定到错误的行数,比如jquery的源码压缩,总共才3行。那样就很难定位到具体的地方了,因为一行有无数浩大的代码。

代码笔者放到了github上:

上述就是本文的全体内容,希望本文的内容对我们的就学或许办事能带来一定的扶助,同时也旨在多多协理脚本之家!

你恐怕感兴趣的稿子:

  • tomcat6下jsp现身getOutputStream() has already been called for this
    response卓殊的缘故和缓解方法
  • 跟自家上学javascript消除异步编制程序万分方案
  • 详解JavaScript中的格外处理方法
  • JavaScript中的卓殊捕捉介绍
  • JS中的极度处理方法分享
  • js中的极度处理try…catch使用介绍
  • js分外捕获方法介绍
  • JS非常处理的三个想方设法(sofish)
  • JSP上传图片发生 java.io.IOException: Stream
    closed格外消除办法
  • javascript
    万分处理利用总计

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website