您的位置:时时app平台注册网站 > web前端 > 前端异常监控

前端异常监控

2019-11-03 04:40

二、JS Error 详细信息解析

 

图片 1  

  统计JS Error的目的,一是为了了解线上项目的健康状况,二是为了分析错误,帮助我们查找问题之所在,并且解决它。所以,为了查找并解决问题,我们需要对几个关键点进行分析。

  ①  某种错误发生的次数——发生次数跟影响用户是成正比的, 如果发生次数跟影响用户数量都很高,那么这是一个比较严重的bug, 需要立即解决。 反之, 如果次数很多,影响用户数量很少。说明这种错误只发生在少量设备中,优先级相对较低,可以择时对该类机型设备进行兼容处理。当然,ip地址访问次数也能说明这个问题

  ②  页面发生了哪些错误——这个有利于我们缩小问题的范围,方便我们排查,如:

  图片 2

  ③  错误堆栈——这点不用说,是定位错误最重要的因素。正常情况下,代码都是被压缩的,所以我在后台解析并截取出错代码附近的一部分代码,进行展示,排查错误。 图片 3

  ④  设备信息——当错误发生是,分析出用户当时使用设备的浏览器信息,系统版本,设备机型等等,能够帮我们快速的定位到需要兼容的设备,进而提升解决问题的效率。

  ⑤  用户足迹——我个人觉得比较有用,但是代价太高。 因为这个需要记录下用户在页面上的所有行为,需要上传非常多的数据,功能待定。

  图片 4

  到此,已经收集到了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));
    }

 

传送门

前端监控异常方案
如何做前端异常监控?
前端代码异常监控

前端异常捕获方法
前端魔法堂——异常不仅仅是try/catch
Capture and report JavaScript errors with window.onerror
GlobalEventHandlers.onerror
addEventListener() 方法,事件监听

平滑应用
gulp-inject

JavaScript Source Map
JavaScript Source Map 详解
使用source-map实现对已压缩发布的前端代码的异常捕获与记录
source-map

Raven.js

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

信息获取
js获取ip地址,操作系统,浏览器版本等信息,可兼容

收集异常信息量太多,怎么办

如果你的网站访问量很大,假如网页的 PV 有 1kw,那么一个必然的错误发送的信息就有 1kw 条,我们可以给网站设置一个采集率:

JavaScript

Reporter.send = function(data) { // 只采集 30% 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)      // 上报错误信息
  }
}

这个采集率可以通过具体实际的情况来设定,方法多样化,可以使用一个随机数,也可以具体根据用户的某些特征来进行判定。

上面差不多是我对前端代码监控的一些理解,说起来容易,但是一旦在工程化运用,难免需要考虑到兼容性等种种问题,读者可以通过自己的具体情况进行调整,前端代码异常监控对于我们的网站的稳定性起着至关重要的作用。如若文中所有不对的地方,还望指正。

  首先,我们应该对Js报错情况有个大致的了解,这样才能够及时的了解前端项目的健康状况。所以我们需要分析出一些必要的数据。

避免addEventListener重复监听

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

try-catch 异常处理

try-catch 在我们的代码中经常见到,通过给代码块进行 try-catch 进行包装后,当代码块发生出错时 catch 将能捕捉到错误的信息,页面也将可以继续执行。

但是 try-catch 处理异常的能力有限,只能捕获捉到运行时非异步错误,对于语法错误和异步错误就显得无能为力,捕捉不到。

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

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="图片 5">


打印如下:

我知道 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/前端异常监控/test.html:22:13
  message:"Unexpected identifier" // 错误信息
  stack:"SyntaxError: Unexpected identifier" // 错误栈
eventPhase:0
filename:"file:///home/jhjr/Desktop/前端异常监控/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"

网上说addEventListener的浏览器兼容性不太好,去Can I use查了一下其实还好.具体看这里addEventListener.

还有一个问题是它的error对象在不同浏览器会有不同的一个体现,这里需要注意下.

跨源资源共享机制( CORS )

首先为页面上的 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')
});

图片 6

读者可通过 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);

因为返回的信息会当做脚本文件来执行,一旦返回的脚本内容出错了,也是无法捕捉到错误的信息。

图片 7

解决办法也不难,跟之前一样,在添加动态添加脚本的时候加上 crossOrigin,并且在后端配上相应的 CORS 字段即可.

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

图片 8

知道原理之后你可能会觉得没什么,不就是给每个动态生成的脚本添加 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 来查看效果:

图片 9

这样重写 createElement 理论上没什么问题,但是入侵了原本的代码,不保证一定不会出错,在工程上还是需要多尝试下看看再使用,可能存在兼容性上问题,如果你觉得会出现什么问题的话也欢迎留言讨论下。

关于 Script error 的问题就写到这里,如果你理解了上面的内容,基本上绝大部分的 Script error 都能迎刃而解。

  如:JS错误类型、 JS错误信息、JS错误堆栈、JS错误发生的位置以及相关位置的代码;JS错误发生的几率、浏览器的类型,版本号,设备机型等等辅助信息

其他问题

  • 压缩代码无法定位到错误具体位置怎么办
    sourceMap解析

  • 用户行为记录

    • 可以在cookie里记录用户在网站的访问记录
    • 对错误发生页面进行页面截屏

    不是很好的方式,当时技术上是可以实现的。

前言

之前在对公司的前端代码脚本错误进行排查,试图降低 JS Error 的错误量,结合自己之前的经验对这方面内容进行了实践并总结,下面就此谈谈我对前端代码异常监控的一些见解。

本文大致围绕下面几点展开讨论:

  1. JS 处理异常的方式
  2. 上报方式
  3. 异常监控上报常见问题

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

错误捕获

  1. 主动捕获(try catch / promise catch)
  2. 全局捕获(onerror / addEventListener)

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

图片 10

如果你嵌入的 iframe 页面和你的主站不是同个域名的,但是 iframe 内容不属于第三方,是你可以控制的,那么可以通过与 iframe 通信的方式将异常信息抛给主站接收。与 iframe 通信的方式有很多,常用的如:postMessage,hash 或者 name 字段跨域等等,这里就不展开了,感兴趣的话可以看:跨域,你需要知道的全在这里

如果是非同域且网站不受自己控制的话,除了通过控制台看到详细的错误信息外,没办法捕获,这是出于安全性的考虑,你引入了一个百度首页,人家页面报出的错误凭啥让你去监控呢,这会引出很多安全性的问题。

*前端性能监控系统:* 预览地址

数据结构

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}

这里参照了我们已有的日志服务的基本数据格式和移动网站的日志上报相关的字段.
主要获取的信息包括:设备信息, 浏览器信息, 用户信息, 错误信息.

示例:异步错误

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

图片 11

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

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

全局捕获

监控上报常见问题

下述例子我全部放在我的 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

一、JS Error 监控功能 (数据概览)

图片 12

 

为了得到这些数据,我们需要在上传的时候将其分析出来。在众多日志分析中,很多字段及功能是重复通用的,所以应该将其封装起来。

// 设置日志对象类的通用属性
  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();

  封装了一个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秒进行一次URL变化的检查,15秒进行一次数据的检查,如果有数据就进行上传,并清空上一次的数据。为什么用定时器呢,因为在单页应用中,路由的切换和地址栏的变化是无法被监控的,我确实没有想到特别好的办法来监控,所以用了这种方式,如果有人有更好的办法,请给我留言,谢谢

 

主动捕获

我们在一些运算之后,得到一个期望的结果,然而结果不是我们想要的,这时可以上报一下错误。
基本上主动捕获也就是要求我们调用Logger.error(error, tag, message)(这个是前端监控js文件提供的一个方法)方法主动上报

示例:运行时错误

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

图片 13

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

  然后,我们再去其中的Js错误进行详细的分析,辅助我们排查出错的位置和发生错误的原因。

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 是无法帮助我们捕获到异常的。

JS 异常处理

对于 Javascript 而言,我们面对的仅仅只是异常,异常的出现不会直接导致 JS 引擎崩溃,最多只会使当前执行的任务终止。

  1. 当前代码块将作为一个任务压入任务队列中,JS 线程会不断地从任务队列中提取任务执行。
  2. 当任务执行过程中出现异常,且异常没有捕获处理,则会一直沿着调用栈一层层向外抛出,最终终止当前任务的执行。
  3. JS 线程会继续从任务队列中提取下一个任务继续执行。
JavaScript

&lt;script&gt; error console.log('永远不会执行'); &lt;/script&gt;
&lt;script&gt; console.log('我继续执行') &lt;/script&gt;

<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>

图片 14

在对脚本错误进行上报之前,我们需要对异常进行处理,程序需要先感知到脚本错误的发生,然后再谈异常上报。

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

下面就谈谈几种异常监控的处理方式:

  对于前端应用来说,Js错误的发生直接影响前端应用的质量。对前端异常的监控是整个前端监控系统中的一个重要环节。 那么如何做到对Js错误的监控呢?对搜集到的js错误,我们该如何去分析呢?分析的结果该如何展示呢?这些问题将直接关系到是否能够监控和分出去有价值的数据。

预期问题

  • 前端错误分类
  • 如何查明出错位置
  • 错误如何上报
  • 错误消息数据结构
  • 如何平滑的应用在业务项目中

异常上报方式

监控拿到报错信息之后,接下来就需要将捕捉到的错误信息发送到信息收集平台上,常用的发送形式主要有两种:

  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 标签进行上报

基本实现

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

  • 错误捕获:

    1. 接口请求error
    2. 全局监听异常
    3. 主动捕获
  • 错误上报:
    这里是需要我们参照app的地方,我们有一个日志服务器专门接收记录日志。

    这里需要我们的上报数据格式和日志服务器一致。

  • 记录日志文件和存入ELK
    我们根据上报的日志数据中两个key生成不同的日志文件,然后将ELK和这个日志文件绑定能够方便我们在kibana上查询。

根据基本流程,可以确定我们每个部分的职责:

  • 前端

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

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

压缩代码如何定位到脚本异常位置

线上的代码几乎都经过了压缩处理,几十个文件打包成了一个并丑化代码,当我们收到 a is not defined 的时候,我们根本不知道这个变量 a 究竟是什么含义,此时报错的错误日志显然是无效的。

第一想到的办法是利用 sourcemap 定位到错误代码的具体位置,详细内容可以参考:Sourcemap 定位脚本错误

另外也可以通过在打包的时候,在每个合并的文件之间添加几行空格,并相应加上一些注释,这样在定位问题的时候很容易可以知道是哪个文件报的错误,然后再通过一些关键词的搜索,可以快速地定位到问题的所在位置。

自动化添加监听文件

通过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'));
});

Promise 错误

通过 Promise 可以帮助我们解决异步回调地狱的问题,但是一旦 Promise 实例抛出异常而你没有用 catch 去捕获的话,onerror 或 try-catch 也无能为力,无法捕捉到错误。

JavaScript

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

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

图片 15

虽然在写 Promise 实例的时候养成最后写上 catch 函数是个好习惯,但是代码写多了就容易糊涂,忘记写 catch。

所以如果你的应用用到很多的 Promise 实例的话,特别是你在一些基于 promise 的异步库比如 axios 等一定要小心,因为你不知道什么时候这些异步请求会抛出异常而你并没有处理它,所以你最好添加一个 Promise 全局异常捕获事件 unhandledrejection。

JavaScript

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

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

图片 16

当然,如果你的应用没有做 Promise 全局异常处理的话,那很可能就像某乎首页这样:

图片 17

为什么要做前端异常监控

  • 有些问题只存在于线上特定的环境
  • 后端错误有监控,前端错误没有监控

示例:语法错误

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

图片 18

一般语法错误在编辑器就会体现出来,常表现的错误信息为: Uncaught SyntaxError: Invalid or unexpected token xxx 这样。但是这种错误会直接抛出异常,常使程序崩溃,一般在编码时候容易观察得到。

错误分类

  1. javascript异常
    • 语法错误
    • 运行时错误
    • script文件内错误(跨域和未跨域)
  2. JS文件、CSS文件、img图片等(资源)的404错误(其实是有onerror事件的dom)
  3. promise的异常捕获
  4. ajax请求错误

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 文件,而是一个 html,这个 html 是银行的登录页面,如果你已经登录了,那 login 页面就会自动跳转到 Welcome xxx...,如果未登录则跳转到 Please Login...,那么报错也会是 Welcome xxx... is not defined,Please Login... is not defined,通过这些信息可以判断一个用户是否登录他的帐号,给入侵者提供了十分便利的判断渠道,这是相当不安全的。

介绍完背景后,那么我们应该去解决这个问题?

首先可以想到的方案肯定是同源化策略,将 JS 文件内联到 html 或者放到同域下,虽然能简单有效地解决 script error 问题,但是这样无法利用好文件缓存和 CDN 的优势,不推荐使用。正确的方法应该是从根本上解决 script error 的错误。

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"

window.onerror 异常处理

window.onerror 捕获异常能力比 try-catch 稍微强点,无论是异步还是非异步错误,onerror 都能捕获到运行时错误。

示例:运行时同步错误

JavaScript

/** * @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

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

图片 21

示例:异步错误

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

图片 22

然而 window.onerror 对于语法错误还是无能为力,所以我们在写代码的时候要尽可能避免语法错误的,不过一般这样的错误会使得整个页面崩溃,还是比较容易能够察觉到的。

在实际的使用过程中,onerror 主要是来捕获预料之外的错误,而 try-catch 则是用来在可预见情况下监控特定的错误,两者结合使用更加高效。

需要注意的是,window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。

图片 23

关于 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">

图片 24

由于网络请求异常不会事件冒泡,因此必须在捕获阶段将其捕捉到才行,但是这种方式虽然可以捕捉到网络请求的异常,但是无法判断 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="">

图片 25

这点知识还是需要知道,要不然用户访问网站,图片 CDN 无法服务,图片加载不出来而开发人员没有察觉就尴尬了。

try catch 捕获
  • 要求程序员在编写代码时,注意基本的异常捕获
  • 自动对JavaScript函数块添加异常捕获利用UglifyJS,使用AST为所有函数加上try catch

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

自动添加try catch仅能对js文件生效,无法对html文件进行操作。(可以在catch中上报关于代码位置)

参考文章

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

    1 赞 2 收藏 评论

如何平滑的应用在业务项目中

原文出处: happylindz   

异常如何上报

监控拿到报错信息之后,接下来就需要将捕捉到的错误信息发送到信息收集平台上,常用的发送形式主要有两种:

  • 通过 Ajax 发送数据(xhr和jquery)
  • 动态创建 img 标签的形式

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

最终选择的还是通过xhr的方式,考虑不想依赖别的script文件。

具体代码

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

本文由时时app平台注册网站发布于web前端,转载请注明出处:前端异常监控

关键词: