background

Traditional mode after a front-end project to formal environment, all error message only by users when using screenshots, verbal description sent to developers, and developers to described according to the user scene to simulate the generation of the error, this must be super low efficiency, so a lot of open source or charge the front-end monitoring platform was born, Our company uses a WebFunny with encrypted code, which cannot be extended twice and can only access the Web Vue version. In this section, we first talk about the monitoring SDK built by wechat small program

Before doing this, I also investigated many other open source SDK and services, but none of them met our company’s requirements:

Monitor SDK: monitor-SDK

Let’s start with JS error collection

How to extend it to SDK? My method is to rewrite -> publish and subscribe to collect errors. Of course, there are many methods, everyone can try

Release subscription

const handlers = {} const appHandles = {} const pageHandles = {} /** * @param {*} handler * @param {*} handleType */ export function subscribeEvent(handler, handleType = '') { if (! handler) { return } switch (handleType) { case 'app': appHandles[handler.type] = appHandles[handler.type] || [] appHandles[handler.type].push(handler.callback) break case 'page': pageHandles[handler.type] = appHandles[handler.type] || [] pageHandles[handler.type].push(handler.callback) break default: handlers[handler.type] = handlers[handler.type] || [] handlers[handler.type].push(handler.callback) } } /** * @param {*}  type * @param {*} data * @param {*} handleType */ export function triggerHandlers(type, data, handleType = '') { switch (handleType) { case 'app': if (! type || ! appHandles[type]) return appHandles[type].forEach((callback) => { callback(data) }) break case 'page': if (! type || ! pageHandles[type]) return pageHandles[type].forEach((callback) => { callback(data) }) break default: if (! type || ! handlers[type]) return handlers[type].forEach((callback) => { callback(data) }) } }Copy the code

Overrides a property on an object:

@param {*} source Specifies the object to be overwritten. @param {*} name Specifies the key of the object to be overwritten. @param {*} replacement replaces the original function. * @param {*} isForced */ export function replaceOld(source, name, replacement, isForced = false) { if (name in source || isForced) { const original = source[name] const wrapped = replacement(original) if (typeof wrapped === 'function') { source[name] = wrapped } } }Copy the code

Rewrite the APP:

const HandleWxPageEvents = { onLoad() { let vm = this.wxMonitor, toUrl = util.getPage() let data = { simpleUrl: toUrl, referrer: vm.referrerPage || "", } vm.logSave('page_pv', data) vm.referrerPage = toUrl } } export function replaceApp(wxMonitor) { if (! App) { return } HandleWxAppEvents.wxMonitor = wxMonitor const originApp = App App = function (appOptions) { let methods = config.APP_CONFIG methods.forEach((method) => { addReplaceHandler({ callback: (data) => HandleWxAppEvents[method.replace('AppOn', 'on')](data), type: method }, 'app') replaceOld( appOptions, method.replace('AppOn', 'on'), function (originMethod) { return function (... args) { triggerHandlers.apply(null, [method, ...args, 'app']) if (originMethod) { originMethod.apply(this, args) } } }, true ) }) return originApp(appOptions) } }Copy the code

Of course Page Page home Page PV and collection error is the same, here is not a paste code

How to collect wx.request to count the number of requests, and how long does the request take

Wx. request can use Object.defineProperty to reattach a description to the Object.

WxMonitor */ export function replaceNetwork(wxMonitor) {let vm = wxMonitor let WxHookMethods = config.WxHookMethods WxHookMethods.forEach(hook => { let originRequest = wx[hook]; Object.defineProperty(wx, hook, { writable: true, enumerable: true, configurable: true, value: function () { let args = []; let startTime = new Date().getTime() for (let _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } let options$1 = args[0]; let url = options$1.url || "" let reqData; if (hook === 'request') { reqData = options$1.data; } let successHandler = function (res) {try {if (!! res && res.statusCode && res.statusCode ! = 200) { let data = { simpleUrl: util.getPage(), httpUrl: options$1.url || "", httpUploadType: config.HTTP_ERROR, responseText: JSON.stringify(res), httpStatus: res.statusCode } if (!! url && url ! = `${vm.queue.baseUrl}${vm.queue.api}`) { vm.logSave('http_log', data) } } else { let endTime = new Date().getTime() let consumeData = { simpleUrl: util.getPage(), loadTime: endTime - startTime, httpUrl: options$1.url || "", httpUploadType: config.HTTP_SUCCESS, responseText: JSON.stringify(res), httpStatus: res.statusCode || 200 } if (!! url && url ! = `${vm.queue.baseUrl}${vm.queue.api}`) { vm.logSave('http_log', consumeData) } } } catch (e) { util.warn('[cloudMonitor] http error') } if (typeof options$1.success === 'function') { return options$1.success(res); }}; let failHandler = function (err) { try { let data = { simpleUrl: util.getPage(), httpUrl: options$1.url || "", httpUploadType: config.HTTP_ERROR, responseText: JSON.stringify(err), httpStatus: '0' } if (!! url && url ! = `${vm.queue.baseUrl}${vm.queue.api}`) { vm.logSave('http_log', data) } } catch (e) { util.warn('[cloudMonitor] http error') } if (typeof options$1.fail === 'function') { return options$1.fail(err); }}; let actOptions = util.__assign(util.__assign({}, options$1), { success: successHandler, fail: failHandler }); return originRequest.call(this, actOptions); }})})}Copy the code

Information collection platform display

I’ll talk about server-side scaffolding later in this article

Server-side Web:Github.com/fonitor/web…Server:Github.com/fonitor/web…