搭建前端监察和控制系统,详解js前端代码相当监察和控制

By admin in 4858美高梅 on 2019年3月31日

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

干什么要做前端至极监察和控制

  • 稍加题目只存在于线上一定的环境
  • 后端错误有监督,前端错误没有监察和控制

初稿出处: happylindz   

开卷目录

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

基本落到实处

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

  • 错误捕获:

    1. 接口请求error
    2. 大局监听至极
    3. 积极抓获
  • 错误报告:
    此间是急需大家参照app的地方,我们有二个日记服务器专门接收记录日志。

    此地须要我们的举报数据格式和日志服务器一致。

  • 笔录日志文件和存入ELK
    作者们依照举报的日志数据中三个key生成差别的日记文件,然后将ELK和这些日志文件绑定能够方便我们在kibana上询问。

据说基本流程,可以规定大家各样部分的职分:

  • 前端

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

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

前言

事先在对集团的前端代码脚本错误实行排查,试图降低 JS Error
的错误量,结合自个儿在此之前的经历对那上边内容开始展览了推行并总计,下边就此谈谈自身对前者代码非常监控的局地意见。

正文大约围绕下边几点展开探究:

  1. JS 处理卓殊的方法
  2. 4858美高梅 ,申报形式
  3. 这一个监察和控制上报常见难点
  • 何以是前者代码很是 
  • window.onerror
  • 写一个js报错的上报库
  • 注意点:
  • 缺点:

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

料想难点

  • 前端错误分类
  • 什么调查出错地方
  • 谬误怎样反馈
  • 漏洞相当多消息数据结构
  • 怎么平滑的应用在作业类型中

JS 万分处理

对于 Javascript 而言,大家面对的仅仅只是卓殊,格外的产出不会平昔促成 JS
引擎崩溃,最八只会使近来实施的职分终止。

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

4858美高梅 1

在对脚本错误实行上报在此之前,大家需求对那么些举办处理,程序须求先感知到脚本错误的发生,然后再谈格外申报。

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

上边就研讨两种尤其监察和控制的处理情势:

在平常的办事,js报错是比较常见的3个现象,越发是有部分荒谬恐怕大家在该地质度量试的时候测试不出去,当宣布到线上未来才足以发现,借使抢救及时,那幸而,倘使很晚才发

  对于前端采取来说,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);
}

4858美高梅 2

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

哪些是前者代码相当 

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

当仁不让抓获

我们在一部分运算之后,得到1个期望的结果,但是结果不是我们想要的,这时能够申报一下不当。
差不离主动抓获也便是供给我们调用Logger.error(error, tag, message)(那个是前者监察和控制js文件提供的3个办法)方法主动举报

示范:语法错误

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);
}

4858美高梅 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);
}

4858美高梅 4

只有你在 setTimeout 函数中再套上一层
try-catch,不然就不能够感知到其错误,但如此代码写起来比较啰嗦。

大家先来说手前端代码非常是怎么着意思。前端代码至极指的是以下两种情形:

  如:JS错误类型、
JS错误消息、JS错误堆栈、JS错误产生的地点以及有关职责的代码;JS错误产生的几率、浏览器的连串,版本号,设备机型等等协理音信

大局捕获

window.onerror 相当处理

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

演示:运转时一并错误

JavaScript

/** * @param {String} msg 错误新闻 * @param {String} url 出错文件 *
@param {Number} row 行号 * @param {Number} col 列号 * @param {Object}
error 错误详细音信 *搭建前端监察和控制系统,详解js前端代码相当监察和控制。/ 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

4858美高梅 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;
});

4858美高梅 6

可是 window.onerror
对于语法错误依旧心有余而力不足,所以大家在写代码的时候要硬着头皮幸免语法错误的,但是貌似那样的一无所长会使得全体页面崩溃,如故相比较简单能够发现到的。

在事实上的运用进程中,onerror 首借使来捕获预料之外的荒谬,而 try-catch
则是用来在可预言情形下监察和控制特定的错误,两者结合使用尤其飞快。

急需注意的是,window.onerror 函数只有在回来 true
的时候,十分才不会进步抛出,不然尽管是精通格外的发出控制台照旧会议及展览示
Uncaught Error: xxxxx。

4858美高梅 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">

4858美高梅 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="">

4858美高梅 9

那点知识可能供给了然,要不然用户访问网站,图片 CDN
不能够服务,图片加载不出来而开发职员没有发现就难堪了。

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

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

4858美高梅 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错误都算3次)/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秒进行1回USportageL变化的检查,15秒实行三遍数据的反省,假诺有多少就开始展览上传,并清空上二遍的数据。为啥用定时器呢,因为在单页应用中,路由的切换和地址栏的变动是力不从心被监察和控制的,小编确实尚未想到特别好的主意来监督,所以用了那种艺术,假诺有人有更好的不二法门,请给自个儿留言,多谢

 

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
是无能为力协助大家捕获到13分的。

Promise 错误

经过 Promise 可以援救大家解决异步回调地狱的题材,不过一旦 Promise
实例抛出13分而你从未用 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’
});

4858美高梅 11

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

故而若是你的运用用到很多的 Promise 实例的话,尤其是你在一部分遵照 promise
的异步库比如 axios
等一定要小心,因为您不知情怎么时候这一个异步请求会抛出非凡而你并不曾处理它,所以您最棒添加1个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’
});

4858美高梅 12

自然,若是你的利用尚未做 Promise
全局13分处理的话,这很或然就如某乎首页那样:

4858美高梅 13

② 、JS脚本在运营时发生错误。

贰 、JS Error 详细信息解析

 

4858美高梅 14  

  总括JS
Error的目标,一是为着打探线上项指标健康境况,二是为了分析错误,扶助大家寻找难题之所在,并且化解它。所以,为了找寻并缓解难题,大家须求对多少个关键点进行分析。

  ① 
某种错误发生的次数——发生次数跟影响用户是成正比的,
如若爆发次数跟影响用户数量都很高,那么那是一个比较严重的bug,
须求及时消除。 反之,
纵然次数过多,影响用户数量很少。表达那种不当只发生在为数不多设备中,优先级相对较低,能够择时对此类机型配备开始展览包容处理。当然,ip地址访问次数也能印证这么些标题

  ② 
页面产生了什么不当——这些便利大家收缩意思的范围,方便我们排查,如:

  4858美高梅 15

  ③ 
错误堆栈——那点并非说,是永恒错误最要紧的因素。符合规律情状下,代码都以被缩小的,所以作者在后台解析并截取出错代码附近的一有的代码,举办展示,排查错误。 4858美高梅 16

  ④ 
设备新闻——当错误产生是,分析出用户马上接纳设备的浏览器音信,系统版本,设备机型等等,能够帮大家不慢的固定到须求十三分的设备,进而升级化解问题的成效。

  ⑤  用户足迹——作者个人认为相比较有用,不过代价太高。
因为那么些要求记录下用户在页面上的具有行为,要求上传万分多的数目,功用待定。

  4858美高梅 17

  到此,已经收集到了JS错误日志的大部音讯了,并且一度分析出JS错误的详细消息了。只供给将其上传,入库,再举办分析突显,就足以看到JS错误新闻的预览效果和详情。所以,大家再去布署一下后台代码。

 

  上一章:
搭建前端监察和控制种类(一)阿里云服务器搭建篇

  下一章:
搭建前端监察和控制系统(三)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="4858美高梅 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.

再有1个题材是它的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。

4858美高梅 19

可通过 npm run nocors 查看效果。

Script error
是浏览器在同源策略限制下产生的,浏览器处于对安全性上的考虑,当页面引用非同域名外部脚本文件时中抛出非凡的话,此时本页面是尚未权利知道那一个报错音信的,取而代之的是出口
Script error 那样的音讯。

4858美高梅 20

这么做的指标是幸免数据走漏到不安全的域中,举个简单的事例,

JavaScript

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

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

上边我们并不曾引入2个 js 文件,而是2个 html,这些 html
是银行的登录页面,如若您曾经报到了,那 login 页面就会活动跳转到
Welcome xxx...,借使未登录则跳转到 Please Login...,那么报错也会是
Welcome xxx... is not defined,Please Login... is not defined,通过那几个消息方可断定一个用户是或不是登录他的帐号,给入侵者提供了拾分有益于的判定渠道,那是一定不安全的。

介绍完背景后,那么我们理应去化解那几个问题?

先是可以想到的方案肯定是同源化策略,将 JS 文件内联到 html
或然放到同域下,固然能简单实用地化解 script error
难题,然则这么不可能选拔好文件缓存和 CDN
的优势,不推荐使用。正确的办法应该是从根本上化解 script error 的荒唐。

4858美高梅 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}

那边参照了我们已有的日记服务的骨干数据格式和活动网站的日志上报相关的字段.
首要取得的音信包蕴:设备消息, 浏览器新闻, 用户新闻, 错误新闻.

跨源能源共享机制( COTiguanS )

第3为页面上的 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’)
});

4858美高梅 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);

因为再次回到的音信会当做脚本文件来推行,一旦回到的脚本内容出错了,也是无能为力捕捉到错误的新闻。

4858美高梅 23

化解办法也简单,跟以前同一,在丰富动态增进脚本的时候添加
crossOrigin,并且在后端配上相应的 COLANDS 字段即可.

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 查看效果

4858美高梅 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 来查看效果:

4858美高梅 25

那样重写 createElement
理论上没什么难点,然则侵犯了本来的代码,不保险一定不会出错,在工程上依然需求多尝试下看看再利用,大概存在包容性上难题,固然您觉得会油但是生什么样难题的话也欢迎留言斟酌下。

有关 Script error
的标题就写到那里,假如你明白了地方的始末,基本上绝一大半的 Script error
都能消除。

那么大家怎样来捕获那种非凡呢,有两种办法,

什么样平滑的采纳在工作类型中

window.onerror 能不能够捕获 iframe 的错误

当您的页面有利用 iframe 的时候,你供给对你引入的 iframe
做丰盛监察和控制的处理,不然一经你引入的 iframe
页面出现了难点,你的主站展现不出去,而你却雾里看花。

率先须要强调,父窗口直接选拔 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 查看效果:

4858美高梅 26

如果您置于的 iframe 页面和你的主站不是同个域名的,不过 iframe
内容不属于第③方,是您能够操纵的,那么能够透过与 iframe
通讯的方法将那贰个新闻抛给主站接收。与 iframe
通讯的法子有很多,常用的如:postMessage,hash 也许 name
字段跨域等等,那里就不举办了,感兴趣的话能够看:跨域,你需求领悟的全在此间

借使是非同域且网站不受自身控制以来,除了通过控制台看到详细的错误音讯外,不能够捕获,那是由于安全性的考虑,你引入了八个百度首页,人家页面报出的荒唐凭啥让你去监察和控制呢,那会引出很多安全性的标题。

第叁种是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'));
});

减弱代码怎么样稳定到脚本非常地方

线上的代码差不多都通过了压缩处理,几13个文本打包成了2个并丑化代码,当大家接到
a is not defined 的时候,大家历来不驾驭这几个变量 a
究竟是怎么着意义,此时报错的谬误日志分明是于事无补的。

先是想到的措施是使用 sourcemap
定位到错误代码的具体位置,详细内容能够参见:Sourcemap
定位脚本错误

除此以外也能够因此在卷入的时候,在每种合并的文件之间添加几行空格,并相应增进有个别申明,那样在定位难点的时候很不难能够清楚是哪个文件报的谬误,然后再经过一些重庆大学词的探寻,能够火速地稳住到标题标所在地方。

其次种是 window.onerror

幸免add伊芙ntListener重复监听

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

征集格外消息量太多,如何做

若果你的网站访问量一点都不小,假使网页的 PV 有
1kw,那么3个毫无疑问的荒唐发送的信息就有 1kw
条,大家得以给网站设置1个采集率:

JavaScript

Reporter.send = function(data) { // 只采集 3/10 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)

八个参数的意思如下:

1、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去示范。

今非昔比浏览器默承认获取的参数值:

4858美高梅 27

写2个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,还有将上报的参数对象合并。并揭破了二个大局方法
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引入外链的价签须求加多3个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
    十分处理利用总计

发表评论

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

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有