浅入浅出前端监控零碎落地

背景

前端代码不像java等后端语言运行在本人的服务器,谬误能够间接捕捉生成日志,前端代码运行在用户端,产生谬误时前端工程师无奈通晓。

这是这套零碎利用前,前端开发者获取用户端错误信息的路径:

做为一名合格的前端工程师,出于对我的项目认真负责的态度(技术驱动),开发了一套本人的前端监控零碎,一方面能够促成我的项目更欠缺,减少用户体验,另一方面,学习其中的谬误捕捉机制,能够让咱们对JavaScript这门语言更多一些理解。

架构

  • 前端SDK

应用TypeScript开发保障代码标准,采纳gulp做为代码打包压缩工具,代码构建后上传至 npm 供前端业务应用。

SDK一些次要API实现:

  1. 对前端资源,img、css、script等谬误捕捉
window.addEventListener('error', function (e) {  let errorData = {    errorType: 'resource',    msg: e.target['localName'],    target: e.target['localName'],    type: e.type,    resourceUrl: e.target['href'] || e.target['currentSrc'] || e.target['src']  };}, true);
  1. 对前端语法错误的捕捉
window.onerror = function (msg, _url, line, col, error) {  setTimeout(function () {    col = col || (window.event && window.event['errorCharacter']) || 0;    let errorData = {      errorType: 'grammar',      msg: error && error.stack ? error.stack.toString() : msg,      resourceUrl: _url,      line: line,      col: col,    }  };};
  1. 对promise谬误的捕捉
window.addEventListener('unhandledrejection', function (e) {    let  resourceUrl,          col,          line,          error = e && e.reason,          message = error.message || '',          stack = error.stack || '',          errs = stack.match(/\(.+?\)/);    if (errs && errs.length) errs = errs[0];    errs = errs.replace(/\w.+[js|html]/g, $1 => { resourceUrl = $1; return ''; });    errs = errs.split(':');    if (errs && errs.length > 1) line = parseInt(errs[1] || 0);    col = parseInt(errs[2] || 0);    let errorData = {      errorType: 'grammar',      msg: message,      resourceUrl: resourceUrl,      line: col,      col: line,      type: e.type    }})
  1. 对ajax谬误的捕捉

尽管 xhr.readyState === 4 写到了正文里,但这里尤为重要,能够判断接口胜利然而非正常的后果

 _Ajax({   onreadystatechange: function (xhr) {      if (xhr.readyState === 4 && xhr.xhr.status === 200) {         //这里能够判断接口胜利然而非正常的后果      }else if(xhr.readyState === 4 && xhr.xhr.status !== 200){                          }   },   onerror: function (xhr) {      ajaxResponse(xhr)   },   onload: function (xhr) {      if (xhr.readyState === 4) {          if (xhr.status < 200 || xhr.status > 300) {              xhr.method = xhr.args.method            }      }   },   open: function (arg, xhr) {      let result = { url: arg[1].split('?')[0], method: arg[0] || 'GET', type: 'xmlhttprequest' }      this.args = result   }})// ajax重写function _Ajax(proxy) {  window['_ahrealxhr'] = window['_ahrealxhr'] || XMLHttpRequest;  //@ts-ignore  XMLHttpRequest = function () {    this.xhr = new window['_ahrealxhr'];    for (var attr in this.xhr) {       var type = "";       try {          type = typeof this.xhr[attr]       } catch (e) { }       if (type === "function") {          this[attr] = hookfun(attr);       } else {          Object.defineProperty(this, attr, {            get: getFactory(attr),            set: setFactory(attr)         })       }    }  }  function getFactory(attr) {     return function () {        var v = this.hasOwnProperty(attr + "_") ? this[attr + "_"] : this.xhr[attr];        var attrGetterHook = (proxy[attr] || {})["getter"]        return attrGetterHook && attrGetterHook(v, this) || v      }  }  function setFactory(attr) {      return function (v) {        var xhr = this.xhr;        var that = this;        var hook = proxy[attr];        if (typeof hook === "function") {          xhr[attr] = function () {             proxy[attr](that) || v.apply(xhr, arguments);          }        } else {          var attrSetterHook = (hook || {})["setter"];          v = attrSetterHook && attrSetterHook(v, that) || v          try {             xhr[attr] = v;          } catch (e) {              this[attr + "_"] = v;          }        }      }    }    function hookfun(fun) {       return function () {          var args = [].slice.call(arguments)          if (proxy[fun] && proxy[fun].call(this, args, this.xhr)) {             return;          }          return this.xhr[fun].apply(this.xhr, args);       }     }     return window['_ahrealxhr'];}
  1. 数据上报:
//1. 应用图片src进行数据上报let sendData = new Image();secdData.src = `${地址?数据}`//2. 应用Navigator.sendBeacon
  • NodeJS服务端

应用经典MVC构造,框架采纳KOA2,pm2进行过程守护,数据存储库应用MySQL

  • 治理后盾React

应用react做为治理后盾

总结

这种业务撑持服务,起源于个人兴趣与思考,没有大量公司资源能够协调,同时也没有了来自下层的压力。本人想做的我的项目,有了更多的自主性,总想着赶快第一版能够上线,感激前端同学JGT(姓名拼音缩写)的激励与配合。

目前第一版已稳固上线,服务于公司泛滥业务,后续还有细节要优化,同时会把一些新的想法加进去。

如果有谬误或者不谨严的中央,请务必给予斧正,非常感激。如果喜爱或者有所启发,欢送star github ,对作者也是一种激励。