前端错误收集(Vue.js、微信小程序)

栏目: JavaScript · 发布时间: 6年前

内容简介:随着公司业务的发展,前端项目也越来越多。有的时候客户反馈的一个问题,需要耗费大量的时间去查。错误信息不能第一时间获取,多少会给公司带来损失。这个时候我们就需要一套错误收集机制,去提前发现代码中存在的问题,在客户反馈之前把问题提前处理掉。或者在收到客户反馈的时候可以查到对应的错误栈来帮助我们快速去定位以及解决问题。下面主要介绍vue和微信小程序错误收集的方法。Vue提供了一个全局配置errorHandler,用于收集Vue运行时发生的错误。 详细介绍api用法:

随着公司业务的发展,前端项目也越来越多。有的时候客户反馈的一个问题,需要耗费大量的时间去查。错误信息不能第一时间获取,多少会给公司带来损失。这个时候我们就需要一套错误收集机制,去提前发现代码中存在的问题,在客户反馈之前把问题提前处理掉。或者在收到客户反馈的时候可以查到对应的错误栈来帮助我们快速去定位以及解决问题。下面主要介绍vue和微信小程序错误收集的方法。

错误收集方法

Vue错误收集

Vue提供了一个全局配置errorHandler,用于收集Vue运行时发生的错误。 详细介绍api cn.vuejs.org/v2/api/#err…

用法:

Vue.config.errorHandler = function (err, vm, info) {
  // handle error
  //`err`是js错误栈信息,可以获取到具体的js报错位置。
  //`vm` vue实例
  //`info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
  // 只在 2.2.0+ 可用
}
复制代码

当然拿到vue对应的实例之后,我们就可以轻松获取到vue对应的组件名称以及自定义属性等等(此处可自由发挥)。

以下是获取组件名称的方法:(来源: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)
            : '')
    );
}
复制代码

这个时候我们代码就可以改写成这样:

Vue.config.errorHandler = function(err, vm, info) {
    if (vm) {
        var componentName = formatComponentName(vm);
        //调用错误日志收集接口
    } else {
        //调用错误日志收集接口
    }
};
复制代码

知道了怎么收集错误信息,接下来只需要简单的包装一下就可以应用到项目中去了。(当然你还需要一个保存错误信息的接口和展示错误信息的界面,网上也有一些成熟的日志处理框架,类似:ELK)

新建一个文件 debug.js

let debugConfig = {
    Vue: null,
    //项目名称
    entryName: 'entryName',
    //脚本版本
    scriptVersion: '1.0',
    // 环境
    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 });
    }
};

// 日志上报
function _logReport({ type, severity, error, metaData, message, lineNumber, columnNumber, fileName }) {

    let { silentDev, Vue } = debugConfig;

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

    //这里可以做一个灰度控制

    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';

    //此处可以给你的页面进行分级
    pageLevel = 'p0';//getPageLevel();

    //此处http请求使用的是vue-resource,可以根据各自的情况进行调整
    Vue.http.post(logReportUrl, {
        entryName,
        scriptVersion,
        message,
        metaData,
        name,
        releaseStage,
        severity,
        stacktrace,
        time,
        title,
        type,
        url,
        client,
        lineNumber,
        columnNumber,
        fileName,
        pageLevel//页面等级
    });

}

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

    debugConfig = Object.assign(debugConfig, { Vue, ...option });
    
    //如果你想在开发环境不去捕获错误信息 可以在此处加上环境判断

    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可以存一些额外数据,比如:用户信息等
                }
            });
        } else {
            debug.notifyError({
                error: err,
                metaData: {
                    userToken: { userId: 1 }//metaData可以存一些额外数据,比如:用户信息等
                }
            });
        }
    };
    
    window.onerror = function(msg, url, lineNo, columnNo, error) {
        debug.notifyError({
            type: 'uncaught', 
            error, 
            metaData: {
                userToken: { userId: 1 }//metaData可以存一些额外数据,比如:用户信息等
            }, 
            message: msg, 
            lineNumber: lineNo, 
            columnNumber: columnNo, 
            fileName: url
        });
    }

}

//最后我们把debug抛到外面供其他地方调用
export { debug }
复制代码

当然你还可以捕获Promise、网络请求、图片等异常。此处推荐一篇比较全的文章,大家可自行去查看 juejin.im/post/5bd2db…

初始化:

//错误日志收集
import debug from './debug';
//初始化错误处理
Vue.use(ngmmdebug, { entryName: 'webmall' });
复制代码

如果你想自己上报错误可以通过:

import { debug } from './debug';
debug.notifyError({ messag: '发生错误了' });
复制代码

微信小程序错误收集

微信小程序收集错误信息也是比较方便的,只需要在调用App函数时传入的对象中实现onError方法即可。文档地址: developers.weixin.qq.com/miniprogram…

用法:

App({
  onError (msg) {
    console.log(msg);//msg就是报错信息
  }
})
复制代码

如果你想让代码移植性更高一点可以通过这样做:

//将App暂存起来
let _App = App;

function HookParams(_appParams, eventName, eventFn) {
  if (_appParams[eventName]) {
    let _eventFn = _appParams[eventName];
    //如果参数中已经存在onError函数,则保留并且添加错误收集
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName);
      return _eventFn.call.apply(_eventFn, [this].concat(Array.prototype.slice.call(arguments)))
    }
  } else {
    //如果参数中不存在onError函数,那比较简单直接定义一个,并且加入错误收集
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName)
    }
  }
}

function onErrorFn(error, eventName) {
  //收集错误
}

App = function (_appParams) {
  HookParams(_appParams, "onError", onErrorFn);
  _App(_appParams);
};
复制代码

原理其实很简单,通过改写App函数,再通过Hook的手段来处理传入的参数即可。

同样,我们现在已经知道了如何收集错误,接下来只需要简单包装一下 我们的代码即可。

新建一个文件 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);
}

// 将App暂存起来
let _App = App;

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

//config
let debugConfig = {
  entryName: 'entryName',
  releaseStage: 'pro',
  scriptVersion: '1.0',
  client: {}
}

//获取设备信息
wx.getSystemInfo({
  success: function (res) {
    debugConfig.client = res;
  }
});

//拼装postData
function getPostData(error = '') {
  let {
    entryName,
    releaseStage,
    scriptVersion,
    client
  } = debugConfig,
  curPage = getCurrentPages()[getCurrentPages().length - 1],
    url = '',
    urlParams = '',
    name = 'error',
    postData = "postData",
    metaData = {},
    pageLevel = 'p0';

  //处理url
  if (curPage) {
    url = curPage.route;

    //此处自己根据实际项目给页面定级
    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;
}

//控制错误的发送
function _logReport(error) {
    //灰度控制自行加
    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;
复制代码

修改app.js:

import ngmmdebug from './utils/ngmmdebug.js';
//初始化
ngmmdebug.init({
  entryName: 'mall-wxapp',
  scriptVersion: '2.6.3'
})
复制代码

手动上报错误

import ngmmdebug from './utils/ngmmdebug.js';
ngmmdebug.notifyError('发生错误了~');
复制代码

小程序有几个比较特别的点可以看一下:

1、通过 wx.getSystemInfo 获取设备信息,当然你还可以通过 wx.getUserInfo 获取用户信息等。

2、小程序完整的链接需要通过页面options属性自行组装。

参考

收集字段参考

{
    "entryName":"项目名称",
    "scriptVersion":"1.0",//脚本版本
    "message":"aler is not defined",//错误描述
    "metaData":{//自定义字段
        "componentName":"anonymous component at /Users/taoxinhua/git/webmall/src/components/app.vue",//组件名称
        "info":"created hook",//vue报错信息
        "userToken":{//用户登录信息
            "user_id": 1
        }
    },
    "name":"ReferenceError",//错误名称
    "releaseStage":"local",//报错环境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)",//错误栈
    "time":1544437068009,//发生错误时的客户端时间
    "title":"年糕妈妈优选",//页面标题
    "type":"caught",//错误类型参考fundebug
    "url":"http://localhost:3200/test",//页面地址
    "client":{//客户端信息
        "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,
        "referrer":"http://localhost:3200/test"
    },
    "pageLevel":"p4"//页面错误级别,方便查询和优先处理重要页面的bug
}
复制代码

以上所述就是小编给大家介绍的《前端错误收集(Vue.js、微信小程序)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms Unlocked

Algorithms Unlocked

Thomas H. Cormen / The MIT Press / 2013-3-1 / USD 25.00

Have you ever wondered how your GPS can find the fastest way to your destination, selecting one route from seemingly countless possibilities in mere seconds? How your credit card account number is pro......一起来看看 《Algorithms Unlocked》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

html转js在线工具
html转js在线工具

html转js在线工具