【錯誤監控】Vue 中利用 errorhandler 錯誤上報二三事

前言

在錯誤監控中,經常使用到的方法有:javascript

  • 全局捕獲 Promise 的未處理異常 unhandledrejection 事件
  • window.onerror 全局捕獲錯誤事件

對於 Vue.js 的錯誤上報須要用其提供的 Vue.config.errorhandler 方法,可是錯誤一旦被這個方法捕獲,就不會外拋到在控制檯。若是須要本身手寫一個錯誤監控,則理論上咱們僅僅將錯誤捕獲並上報,但最好不要阻止錯誤在控制檯中展現,因此本文來分析下當中的二三事。vue

若有興趣,或者您正在爲本身的項目寫錯誤上報的插件,能夠一塊兒來分析看看。本文咱們將從錯誤監控的視角來分析和使用 errorhandlerjava

vue 項目錯誤正常展現

示例代碼以下:git

export default {
    created() {
        let a = null;
        if(a.length > 1) {
            // ...
        }
    }
};

正常狀況下,上述代碼會報錯:image.pnggithub

vue 項目錯誤捕獲以及當中的缺陷

繼上面後,咱們嘗試經過 Vue.config.errorHander 捕獲:函數

Vue.config.errorHandler = (err, vm, info) => {
    console.log('進來啦~');
}

export default {
    created() {
        let a = null;
        if(a.length > 1) {
            // ...
        }
    }
};

而後控制檯就不會對外拋錯:
image.png源碼分析

那麼在錯誤監控系統中,理論上咱們只去捕獲報錯而不去攔截報錯,那麼要怎麼作才能把錯誤外拋到控制檯呢?ui

簡單點,就加個console.error(err)this

Vue.config.errorHandler = (err, vm, info) => {
    console.log('進來啦~');
    console.error(err);~~~~
}

export default {
    created() {
        let a = null;
        if(a.length > 1) {
            // ...
        }
    }
};

控制檯將會獲得下面截圖:
image.pngspa

那麼這樣就能夠了,錯誤的上報咱們能夠再Vue.config.errorHandler 中去作,完了再去把 Vue 中的這個「短板」補上,讓錯誤繼續正常拋出來。那麼此時錯誤上報的函數(好比這函數是是captureError())應該這麼去調用:

Vue.config.errorHandler = (err, vm, info) => {
    console.log('進來啦~');
    // 錯誤上報到收集報錯的平臺
    captureError(err);
}

export default {
    created() {
        let a = null;
        if(a.length > 1) {
            // ...
        }
    }
};

errorHandler 的源碼分析

爲了弄清楚 Vue 中對於報錯是怎麼處理的,咱們來看看源碼(註釋也順便寫了):

function globalHandleError (err, vm, info) {
  // 獲取全局配置,判斷是否設置處理函數,默認undefined
  // 已配置
  if (config.errorHandler) {
    try {
      // 執行 errorHandler
      return config.errorHandler.call(null, err, vm, info)
    } catch (e) {
      // \# 若是開發者在errorHandler函數中手動拋出一樣錯誤信息throw err  \# 判斷err信息是否相等,避免log兩次
      if (e !== err) {
        logError(e, null, 'config.errorHandler')
      }
    }
  }
  // 沒有配置,常規輸出
  logError(err, vm, info)
}

function logError (err, vm, info) {
  if (process.env.NODE_ENV !== 'production') {
    warn(`Error in ${info}: "${err.toString()}"`, vm)
  }
  /* istanbul ignore else */
  if ((inBrowser || inWeex) && typeof console !== 'undefined') {
    console.error(err)
  } else {
    throw err
  }
}

源碼中解讀 Bugsnag-js 和 sentry 中如何處理的?

先來看 bugsnag-js 中的作法:

module.exports = {
  name: 'vue',
  init: (client, Vue = window.Vue) => {
    if (!Vue) throw new Error('cannot find Vue')
    
    // 思考爲何這樣作
    const prev = Vue.config.errorHandler

    const handler = (err, vm, info) => {
      const handledState = { severity: 'error', unhandled: true, severityReason: { type: 'unhandledException' } }
      const report = new client.BugsnagReport(err.name, err.message, client.BugsnagReport.getStacktrace(err), handledState, err)

      report.updateMetaData('vue', {
        errorInfo: info,
        component: vm ? formatComponentName(vm, true) : undefined,
        props: vm ? vm.$options.propsData : undefined
      })

      client.notify(report)
      if (typeof console !== 'undefined' && typeof console.error === 'function') console.error(err)

      if (typeof prev === 'function') prev.call(this, err, vm, info)
    }
    
    // 思考爲何這樣作
    Vue.config.errorHandler = handler
    return null
  }
}

再來看 sentry 中的作法:

function vuePlugin(Raven, Vue) {
  Vue = Vue || window.Vue;

  // quit if Vue isn't on the page
  if (!Vue || !Vue.config) return;
  
  // 爲何這麼作?
  var _oldOnError = Vue.config.errorHandler;
  Vue.config.errorHandler = function VueErrorHandler(error, vm, info) {
    
    // ...

    // 上報
    Raven.captureException(error, {
      extra: metaData
    });

    if (typeof _oldOnError === 'function') {
      // 爲何這麼作?
      _oldOnError.call(this, error, vm, info);
    }
  };
}

module.exports = vuePlugin;

以上。

相關文章
相關標籤/搜索