preface

As the company’s business grew, so did the front-end projects. Sometimes a customer feedback a problem, need to spend a lot of time to check. Error information is not the first time to obtain, how much will bring losses to the company. At this point, we need a bug collection mechanism to find problems in the code in advance and deal with them before the customer feedback. Or when receiving customer feedback, we can find the corresponding error stack to help us quickly locate and solve the problem. The following mainly introduces vue and wechat small program error collection methods.

Error collection method

Vue error collection

Vue provides a global configuration errorHandler to collect errors that occur while Vue is running. API cn.vuejs.org/v2/api/#err…

Usage:

Vue.config.errorHandler = function (err, vm, info) {
  // handle error
  // 'err' is the js error stack information, you can get the specific JS error location.
  / / ` vm ` vue instance
  // 'info' is Vue specific error information, such as the lifecycle hook where the error occurred
  // Only available in 2.2.0+
}
Copy the code

Of course, once we get the vUE instance, we can easily get the vUE component name, custom properties and so on (free play here).

Here’s how to get the component name: (source: fundebug)

function formatComponentName(vm) {
    if (vm.$root === vm) return 'root';
    var name = vm._isVue
        ? (vm.$options && vm.$options.name) ||
        (vm.$options && vm.$options._componentTag)
        : vm.name;
    return (
        (name ? 'component <' + name + '>' : 'anonymous component') +
        (vm._isVue && vm.$options && vm.$options.__file
            ? ' at ' + (vm.$options && vm.$options.__file)
            : ' ')); }Copy the code

At this point our code can be rewritten like this:

Vue.config.errorHandler = function(err, vm, info) {
    if (vm) {
        var componentName = formatComponentName(vm);
        // Invoke the error log collection interface
    } else {
        // Invoke the error log collection interface}};Copy the code

Now that you know how to collect error information, you can simply wrap it up and apply it to your project. (Of course, you also need an interface for storing and displaying error messages. There are some mature logging frameworks on the web, such as ELK.)

Create a new file, debug.js

let debugConfig = {
    Vue: null.// Project name
    entryName: 'entryName'.// Script version
    scriptVersion: '1.0'./ / environment
    releaseStage: 'pro'
},
debug = {
    notifyWarn({ message, metaData }) {
        let type = 'caught',
            severity = 'warn';
        
        _logReport({ type, severity, message, metaData });
    },
    notifyError({ type = 'caught', error, message, metaData, lineNumber, columnNumber, fileName }){
        let severity = 'error'; _logReport({ type, severity, error, metaData, message, lineNumber, columnNumber, fileName }); }};// Logs are reported
function _logReport({ type, severity, error, metaData, message, lineNumber, columnNumber, fileName }) {

    let { silentDev, Vue } = debugConfig;

    message = message || error && error.message || ' ';

    // We can do a grayscale control here

    let { entryName, releaseStage, severity, scriptVersion } = debugConfig,
        name = error && error.name || 'error',
        stacktrace = error && error.stack || ' ',
        time = Date.now(),
        title = document.title,
        url = window.location.href,
        client = {
            userAgent: window.navigator.userAgent,
            height: window.screen.height,
            width: window.screen.width,
            referrer: window.document.referrer
        },
        pageLevel = 'p4';

    // Here you can rank your pages
    pageLevel = 'p0';//getPageLevel();

    // The HTTP request here uses vue-resource, which can be adjusted to suit your situation
    Vue.http.post(logReportUrl, {
        entryName,
        scriptVersion,
        message,
        metaData,
        name,
        releaseStage,
        severity,
        stacktrace,
        time,
        title,
        type,
        url,
        client,
        lineNumber,
        columnNumber,
        fileName,
        pageLevel// Page level
    });

}

export default function(Vue, option = {}){

    debugConfig = Object.assign(debugConfig, { Vue, ... option });// If you want to avoid catching error messages in the development environment, you can add environment judgment here

    function formatComponentName(vm) {
        if (vm.$root === vm) return 'root';
        let name = vm._isVue
            ? (vm.$options && vm.$options.name) ||
            (vm.$options && vm.$options._componentTag)
            : vm.name;
        return (
            (name ? 'component <' + name + '>' : 'anonymous component') +
            (vm._isVue && vm.$options && vm.$options.__file
                ? ' at ' + (vm.$options && vm.$options.__file)
                : ' ')); } Vue.config.errorHandler =function(err, vm, info) {
        if (vm) {
            let componentName = formatComponentName(vm);
            let propsData = vm.$options && vm.$options.propsData;
            debug.notifyError({
                error: err,
                metaData: {
                    componentName,
                    propsData,
                    info,
                    userToken: { userId: 1 }//metaData stores additional data, such as user information}}); }else {
            debug.notifyError({
                error: err,
                metaData: {
                    userToken: { userId: 1 }//metaData stores additional data, such as user information}}); }};window.onerror = function(msg, url, lineNo, columnNo, error) {
        debug.notifyError({
            type: 'uncaught', 
            error, 
            metaData: {
                userToken: { userId: 1 }//metaData stores additional data, such as user information
            }, 
            message: msg, 
            lineNumber: lineNo, 
            columnNumber: columnNo, 
            fileName: url }); }}// Finally we throw debug out for call elsewhere
export { debug }
Copy the code

You can also catch exceptions like promises, network requests, images, and so on. Here recommend a more complete article, we can go to see juejin.cn/post/684490…

Initialization:

// Error log collection import debug from'./debug'; Vue. Use (debug, {entryName:'webmall' });
Copy the code

If you want to report an error yourself, you can do so by:

import { debug } from './debug';
debug.notifyError({ messag: 'An error has occurred' });
Copy the code

Wechat applets error collection

It is also convenient for wechat applet to collect error information. It only needs to implement onError method in the object passed in when calling App function. Address: document developers.weixin.qq.com/miniprogram…

Usage:

App({
  onError (msg) {
    console.log(msg);// MSG is an error message}})Copy the code

If you want to make your code more portable you can do this:

// Store the App temporarily
let _App = App;

function HookParams(_appParams, eventName, eventFn) {
  if (_appParams[eventName]) {
    let _eventFn = _appParams[eventName];
    // If the onError function already exists in the argument, keep and add the error collection
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName);
      return _eventFn.call.apply(_eventFn, [this].concat(Array.prototype.slice.call(arguments)))}}else {
    // If the onError function does not exist, it is easier to define one and add the error collection
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName)
    }
  }
}

function onErrorFn(error, eventName) {
  // Collection error
}

App = function (_appParams) {
  HookParams(_appParams, "onError", onErrorFn);
  _App(_appParams);
};
Copy the code

The principle is actually very simple, by rewriting the App function, and then through Hook means to deal with the incoming parameters.

Again, now that we know how to collect errors, we can simply wrap up our code.

Create a new file, debug.js

function HookParams(_appParams, eventName, eventFn) {
  if (_appParams[eventName]) {
    let _eventFn = _appParams[eventName];
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName);
      return _eventFn.call.apply(_eventFn, [this].concat(Array.prototype.slice.call(arguments)))}}else {
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName)
    }
  }
}

function objToParam(options = {}) {
    let params = ' ';
    for (let key in options) {
        params += '&' + key + '=' + options[key];
    }
    return params.substring(1);
}

function onErrorFn(error, eventName) {
    _logReport(error);
}

// Store the App temporarily
let _App = App;

App = function (_appParams) {
  HookParams(_appParams, "onError", onErrorFn);
  _App(_appParams);
};

//config
let debugConfig = {
  entryName: 'entryName'.releaseStage: 'pro'.scriptVersion: '1.0'.client: {}}// Obtain device information
wx.getSystemInfo({
  success: function (res) { debugConfig.client = res; }});/ / assembled postData
function getPostData(error = ' ') {
  let {
    entryName,
    releaseStage,
    scriptVersion,
    client
  } = debugConfig,
  curPage = getCurrentPages()[getCurrentPages().length - 1],
    url = ' ',
    urlParams = ' ',
    name = 'error',
    postData = "postData",
    metaData = {},
    pageLevel = 'p0';

  / / processing the url
  if (curPage) {
    url = curPage.route;

    // Here you grade the page according to the actual project
    pageLevel = 'p0'; //getPageLevel(url);

    urlParams = objToParam(curPage.options);
    if (urlParams) {
      url += '? ' + urlParams;
    }
  }

  name = error.split('\n') [0] | |'error';
  metaData = {
    userToken: getHeaders()
  }

  try {
    postData = {
      data: JSON.stringify({
        entryName,
        type: 'caught',
        scriptVersion,
        releaseStage,
        name,
        stacktrace: error,
        time: Date.now(),
        client,
        url,
        metaData,
        pageLevel,
        serviceLevel
      })
    };
  } catch (e) {
    console.error(e);
  }

  return postData;
}

// Control error sending
function _logReport(error) {
    // grayscale control automatically add
    wx.request({
        header: {
        'content-type': 'application/x-www-form-urlencoded'
        },
        method: 'POST'.url: logReportUrl,
        data: getPostData(error)
    });
}

let debug = {
  init(option = {}) {
    debugConfig = Object.assign({}, debugConfig, option);
  },
  notifyError(error) {
    _logReport(error)
  }
}

module.exports = debug;
Copy the code

Modify the app. Js:

import debug from './utils/debug.js';
/ / initialization
debug.init({
  entryName: 'mall-wxapp'.scriptVersion: '2.6.3'
})
Copy the code

Manual error reporting

import debug from './utils/debug.js';
debug.notifyError('Error ~');
Copy the code

Applets have a few special points to look at:

1. Get device information from wx.getSystemInfo. Of course, you can also get user information from wx.getUserInfo.

2, small program complete links need to be self-assembled through the page options property.

reference

Collect field reference

{"entryName":" project name ", "ScriptVersion ":"1.0",// scriptVersion" message":"aler is not defined",// error description "metaData":{// custom field "componentName":"anonymous" Component at/Users/taoxinhua/git/webmall/SRC/components/app. Vue ", / / component name "info", "created the hooks", / / the vue error message "UserToken ":{// User login information "user_id": 1}}, "name" : "ReferenceError," name "releaseStage" / / error: "local", / / an error environment pre | beta | local "stacktrace" : "ReferenceError: aler is not defined at VueComponent.created (webpack-internal:///370:32:9) at callHook (webpack-internal:///1:2666:21) at VueComponent.Vue._init (webpack-internal:///1:4227:5) at new VueComponent (webpack-internal:///1:4397:12) at createComponentInstanceForVnode (webpack-internal:///1:3679:10) at init (webpack-internal:///1:3496:45) at createComponent (webpack-internal:///1:5148:9) at createElm (webpack-internal:///1:5091:9) at Vue$3.patch [as __patch__] (webpack-internal:// 1:5607:9) at Vue$3.vue. _update (webpack-internal:// 1:2415:19)",// Error stack "Time ":1544437068009,// Client time when an error occurs "title":" page title",// Page title" type":"caught",// Refer to fundebug for the error type "Url" : "http://localhost:3200/test", / / page address "client" : {/ / client information "userAgent" : "Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", "height":800, "Width" : 1280, "the referrer" : "http://localhost:3200/test"}, "pageLevel" : "p4" / / level of a page fault, convenient query bug} and prioritize the main pageCopy the code

Easier to use log collection system

If your system is small and you want to use it for free, try Fundebug.

conclusion

Front-end error collection is relatively simple, how to make good use of these collected errors need to slowly consider themselves. If there is anything wrong with the article, please point it out in the comments section.