浅入浅出前端监控零碎落地
背景
前端代码不像 java 等后端语言运行在本人的服务器,谬误能够间接捕捉生成日志,前端代码运行在用户端,产生谬误时前端工程师无奈通晓。
这是这套零碎利用前,前端开发者获取用户端错误信息的路径:
做为一名合格的前端工程师,出于对我的项目认真负责的态度(技术驱动),开发了一套本人的前端监控零碎,一方面能够促成我的项目更欠缺,减少用户体验,另一方面,学习其中的谬误捕捉机制,能够让咱们对 JavaScript 这门语言更多一些理解。
架构
- 前端 SDK
应用 TypeScript 开发保障代码标准,采纳 gulp 做为代码打包压缩工具,代码构建后上传至 npm 供前端业务应用。
SDK 一些次要 API 实现:
- 对前端资源,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);
- 对前端语法错误的捕捉
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,
}
};
};
- 对 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
}
})
- 对 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. 应用图片 src 进行数据上报
let sendData = new Image();
secdData.src = `${地址? 数据}`
//2. 应用 Navigator.sendBeacon
- NodeJS 服务端
应用经典 MVC 构造,框架采纳 KOA2,pm2 进行过程守护,数据存储库应用 MySQL
- 治理后盾 React
应用 react 做为治理后盾
总结
这种业务撑持服务,起源于个人兴趣与思考,没有大量公司资源能够协调,同时也没有了来自下层的压力。本人想做的我的项目,有了更多的自主性,总想着赶快第一版能够上线,感激前端同学 JGT(姓名拼音缩写)的激励与配合。
目前第一版已稳固上线,服务于公司泛滥业务,后续还有细节要优化,同时会把一些新的想法加进去。
如果有谬误或者不谨严的中央,请务必给予斧正,非常感激。如果喜爱或者有所启发,欢送 star github,对作者也是一种激励。