フロントエンドエラー収集(Vue.js、微信ウィジェット)

12114 ワード

フロントエンドエラー収集(Vue.js、微信ウィジェット)
前言
会社の業務の発展に伴い、先端プロジェクトもますます増えている.お客様からのフィードバックの問題は、多くの時間を費やして調べる必要があります.エラーメッセージは最初に取得できません.多少は会社に損失をもたらします.このとき、コードに存在する問題を事前に発見し、お客様のフィードバックの前に問題を事前に処理するエラー収集メカニズムが必要です.あるいは、お客様からのフィードバックを受け取ったときに、対応するエラースタックを調べて、迅速な位置決めと問題解決を支援することができます.以下では主にvueと微信ウィジェットのエラー収集の方法を紹介する.
エラー収集方法
Vueエラー収集
Vueは、Vueの実行中に発生したエラーを収集するグローバル構成errorHandlerを提供します.詳しくは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 ' : '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 ' : '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('
')[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
}

使いやすいログ収集システム
システムが小さくて無料で使いたいならfundebugを試してみてください.