UNPKG

monitor-fe

Version:
619 lines (552 loc) 16.6 kB
/*! * monitor-fe.js v1.4.5 * (c) 2021 fangyuan <735512174@qq.com> * Released under the MIT License. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global['monitor-fe'] = factory()); }(this, (function () { 'use strict'; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** * 前端监控错误上报工具 */ var Utils = require('./utils.js'); /** *前端监控器 * * @class WebMonitor */ var WebMonitor = function () { /** * Creates an instance of WebMonitor. * @param {*} params * @memberof WebMonitor */ function WebMonitor(params) { classCallCheck(this, WebMonitor); this.utils = new Utils(); this.baseOptions = { //基础参数 systemName: '', //系统名称 reportUrl: '', //监控请求地址 delayTime: 10000, //延迟上报时间 whiteList: [], //上报白名单 userId: '', //用户id shopId: '', //店铺id vue: false, //是否需要引入vue mini: false, //是否是小程序接入:默认是在小程序环境里,为了兼容小程序老代码 wx: null, //微信对象 ajax: null, //外部传入的ajax方法 getUserAgent: null //外部传入的设备数据方法 }; this.options = {}; this.setOption(params); if (!this.options.systemName) { console.warn('前端监控器缺少systemName参数,无法运行,请检查!'); return; } if (!this.options.reportUrl) { console.warn('前端监控器缺少reportUrl参数,无法运行,请检查!'); return; } this.cacheQuene = []; //缓冲队列 this.__init(); } /** * 设置配置参数 * * @param {*} options 外部配置的参数集合(userId,shopId, 以及其他自定义的参数会发送到服务器) * @memberof WebMonitor */ createClass(WebMonitor, [{ key: 'setOption', value: function setOption(options) { if (!this.utils.isObject(options)) { console.warn('前端监控器初始化参数格式错误,请检查!'); return; } this.options = Object.assign({}, this.baseOptions, this.options, options); if (this.options.vue) { this.Vue = require('vue'); } } /** *初始化 * * @memberof WebMonitor */ }, { key: '__init', value: function __init() { var _this = this; this.__getWhiteList(function (data) { if (!data) { console.warn('获取白名单失败!'); } _this.__getBrowserInfo(); //浏览器信息 _this.__initQuene(); _this.__vueError(); _this.__consoleError(); _this.__promiseError(); }); } /** *设置白名单 * * @memberof WebMonitor */ }, { key: '__getWhiteList', value: function __getWhiteList(_callback) { var _this2 = this; var _self = this; var whiteListUrl = 'https://omo.aiyouyi.cn/common-static/monitor-white-list.json?v=' + new Date().getTime(); if (!this.options.mini) { //浏览器ajax获取白名单 this.request({ url: whiteListUrl, method: 'GET', data: null, callback: function callback(data) { if (data) { if (_this2.utils.isString(data)) { data = JSON.parse(data); } _self.__setWhiteList(data); _callback && _callback(data); } else { _callback && _callback(null); } } }); } else { //小程序获取白名单 this.options.wx && this.options.wx.request({ url: whiteListUrl, method: 'get', headers: { 'Content-Type': 'application/json;charset=UTF-8', 'accept': 'application/json, text/plain, */*' }, success: function success(data) { _self.__setWhiteList(data.data); _callback && _callback(data.data); }, fail: function fail(error) { _callback && _callback(null); } }); } } /** * 设置白名单 * * @param {*} list * @memberof WebMonitor */ }, { key: '__setWhiteList', value: function __setWhiteList(list) { var _list = []; if (this.utils.isObject(list)) { for (var key in list) { _list.push(list[key]); } } else { _list = list; } if (_list && Array.isArray(_list)) { this.options.whiteList = _list; } } /** *初始化延迟合并队列 * * @memberof WebMonitor */ }, { key: '__initQuene', value: function __initQuene() { var _self = this; setInterval(function () { _self.__removeDuplicate(_self.cacheQuene).forEach(function (item) { _self.__send(item); }); _self.cacheQuene = []; }, this.options.delayTime); } /** *发送队列去重 * * @param {*} array 发送队列 * @return {*} * @memberof WebMonitor */ }, { key: '__removeDuplicate', value: function __removeDuplicate(array) { var result = []; for (var i = 0, l = array.length; i < l; i++) { for (var j = i + 1; j < l; j++) { if (array[i].errorMessage === array[j].errorMessage) { j = ++i; } } result.push(array[i]); } return result; } /** * * @param {*} type 上报错误类型 * @param {*} msg 消息文本 * @param {*} requestInfo 请求信息 * @memberof WebMonitor */ }, { key: '__report', value: function __report(type, msg, requestInfo) { //如果有自定义参数,则加上自定义参数 var options = {}; for (var key in this.options) { if (!Object.keys(this.baseOptions).includes(key)) { options[key] = this.options[key]; } } this.cacheQuene.push({ pageUrl: this.options.pageUrl || (typeof window !== 'undefined' ? window.location.href : ''), systemName: this.options.systemName, errorType: type, errorTime: this.utils.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss'), userAgent: this.userAgent, userId: this.options.userId, shopId: this.options.shopId, requestInfo: requestInfo || {}, errorMessage: msg, customInfo: options }); } /** * 发送http请求上报 * * @param {*} data 发送的数据 * @memberof WebMonitor */ }, { key: '__send', value: function __send(data) { var msg = data.errorMessage; for (var i = 0; i < this.options.whiteList.length; i++) { var one = this.options.whiteList[i]; if (one.includes('$$')) { //如果白名单项是正则对象则匹配屏蔽 one = one.match(/[^【]+(?=】)/g); var _Reg = new RegExp(one[0], one[1] || ''); if (_Reg.test(msg)) { return; } } else if (this.utils.isString(one)) { //如果白名单是字符串则包含屏蔽 if (msg.includes(one)) { return; } } } if (this.options.ajax && this.utils.isFunction(this.options.ajax)) { this.options.ajax(data); return; } else { if (!this.options.mini) { //浏览器ajax上报错误 this.request({ url: this.options.reportUrl, method: 'POST', data: JSON.stringify(data) }); } else { //小程序上报错误 this.options.wx && this.options.wx.request({ url: this.options.reportUrl, method: 'post', data: Object.assign({}, data, { pageUrl: this.options.pageUrl }) }); } } } /** * 浏览器请求远端数据 * * @param {*} url * @param {*} method * @param {*} data * @param {*} callback * @memberof WebMonitor */ }, { key: 'request', value: function request(_ref) { var url = _ref.url, method = _ref.method, data = _ref.data, callback = _ref.callback; var versionList = ["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", "Microsoft.XMLHttp"]; var xhr; if (XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { for (var i = 0; i < versionList.length; i++) { try { xhr = new ActiveXObject(versionList[i]); break; } catch (e) { console.warn(e); } } } xhr.onreadystatechange = function (myxhr) { return function () { if (myxhr.readyState === 4 && myxhr.status === 200) { callback && callback(myxhr.responseText); } else if (myxhr.status >= 400) { callback && callback(null); } }; }(xhr); xhr.open(method, url, true); xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8"); xhr.setRequestHeader("Accept", "*/*"); xhr.send(data); } /** *对外提供的错误发送方法 * * @param {*} {type, error} type:错误类型 error:错误内容 * @memberof WebMonitor */ }, { key: 'emit', value: function emit(_ref2) { var type = _ref2.type, error = _ref2.error; if (!type) { console.warn('未定义错误类型'); return; } //如果是请求错误,带上请求参数 if (type === 'httpError') { var requestInfo = this.__httpError(error); this.__report(type, this.__createMessage({ error: error }), requestInfo); } else { //其他自定义错误事件,直接上报 this.__report(type, this.__createMessage({ error: error })); } } /** *创建消息 * * @param {*} { componentInfo:vue组件路径/组件名称, error:错误对象/错误字符串/axios的response } * @return {*} 生成的消息体 * @memberof WebMonitor */ }, { key: '__createMessage', value: function __createMessage(_ref3) { var componentInfo = _ref3.componentInfo, error = _ref3.error; //消息内容 var errorMessage = ''; //收集消息 if (this.utils.isErrorObj(error)) { errorMessage = error.stack ? error.stack.toString() : error.message ? error.message : error || '未知错误'; } else if (this.utils.isString(error)) { errorMessage = error; } else if (this.utils.isAxiosResponseObj(error)) { errorMessage = 'code:' + (error.data.code || '--') + ',msg:' + error.data.msg + ',status:' + error.data.status; } //如果是vue组件,则可以带上组件路径或名称 if (componentInfo) { errorMessage = '[componentInfo]: ' + componentInfo + ', ' + errorMessage; } //此行不可删除,截获信息以后在浏览器里显示 console.log('!!!monitorError: ' + errorMessage); return errorMessage; } /** *注册vue实例错误事件 * * @memberof WebMonitor */ }, { key: '__vueError', value: function __vueError() { var _self = this; var errorType = 'vueError'; if (!this.Vue) { console.warn('Vue不存在!'); return; } var vue = this.Vue.default || this.Vue; vue.config.errorHandler = function (error, vm, info) { var componentInfo = vm._isVue ? vm.$options.__file || vm.$options.name || vm.$options._componentTag : vm.name; _self.__report(errorType, _self.__createMessage({ componentInfo: componentInfo || '', error: error })); }; } /** *注册控制台错误事件 * * @memberof WebMonitor */ }, { key: '__consoleError', value: function __consoleError() { var errorType = 'consoleError'; if (typeof window === 'undefined') { return; } if (!window.console || !window.console.error) { return; } var _self = this; var consoleError = window.console.error; window.console.error = function (error) { _self.__report(errorType, _self.__createMessage({ error: error })); consoleError && consoleError.apply(window, arguments); }; } /** *注册promise错误事件 * * @memberof WebMonitor */ }, { key: '__promiseError', value: function __promiseError() { var errorType = 'promiseError'; var _self = this; if (typeof window === 'undefined') { return; } window.addEventListener('unhandledrejection', function (error) { if (error.reason) { _self.__report(errorType, _self.__createMessage({ error: error.reason })); } }); } /** * * * @param {*} error 错误信息 * @return {*} * @memberof WebMonitor */ }, { key: '__httpError', value: function __httpError(error) { var requestInfo = {}; if (!error.config) { return requestInfo; } requestInfo = { url: error.request && error.request.responseURL ? error.request.responseURL : '', method: error.config.method }; if (error.config.method.toLowerCase() === 'post' && error.config.data) { var rusult = {}; if (this.utils.isString(error.config.data)) { var decodeString = decodeURI(error.config.data); if (decodeString.includes('json=')) { rusult = JSON.parse(decodeString.split('json=')[1]); } else { rusult = JSON.parse(decodeString); } } else if (this.utils.isObject(error.config.data)) { rusult = error.config.data; } requestInfo['data'] = rusult; } if (error.config.method.toLowerCase() === 'get' && error.config.params) { if (this.utils.isString(error.config.params)) { requestInfo['params'] = JSON.parse(error.config.params); } else if (this.utils.isObject(error.config.data)) { requestInfo['params'] = error.config.params; } } requestInfo['headers'] = error.config.headers; return requestInfo; } /** *获取浏览器信息(浏览器名称+版本) * * @return {*} * @memberof WebMonitor */ }, { key: '__getBrowserInfo', value: function __getBrowserInfo() { if (this.options.getUserAgent && this.utils.isFunction(this.options.getUserAgent)) { this.userAgent = this.options.getUserAgent(); return; } var browser = { msie: false, firefox: false, opera: false, safari: false, chrome: false, netscape: false, name: 'unknown', version: 0 }; if (typeof window === 'undefined') { return; } var userAgent = window.navigator.userAgent.toLowerCase(); if (/(msie|firefox|opera|chrome|netscape)\D+(\d[\d.]*)/.test(userAgent)) { browser[RegExp.$1] = true; browser.name = RegExp.$1; browser.version = RegExp.$2; } else if (/version\D+(\d[\d.]*).*safari/.test(userAgent)) { // safari browser.safari = true; browser.name = 'safari'; browser.version = RegExp.$2; } this.userAgent = browser.name + '-' + browser.version; } }]); return WebMonitor; }(); return WebMonitor; })));