前端错误日志上报相关实践

1,467 阅读3分钟
  1. 传统的前端页面错误捕获直接使用

    window.onerror = function (msg, url, lineNo, columnNo, error) {
        console.log(msg)
        console.log(url)
      }
    window.addEventListener('error', (msg, url, row, col, error) => {
        console.log('我知道错了')
        console.log(msg, url, row, col, error)
        return true
      }, true)

    这样就可以实现大部分错误的捕获,但是在MVVM时代这样的捕获方式不可行的,因为无论是Vue还是React都有自己捕获错误的方法,在Vue中,其本身提供了一种方式进行Vue页面的错误捕获

    Vue.config.errorHandler = function (err, vm, info) {
        if (vm) {
          let componentName = formatComponentName(vm);
          let propsData = vm.$options && vm.$options.propsData;
          console.log(componentName)
          console.log(propsData)
          let {
            message, // 异常信息
            name, // 异常名字
            script, // 异常脚本url
            line, // 异常行号
            colum, // 异常列号
            stack // 异常堆栈信息
          } = err
          console.log(stack)
        } else {
          
        }
      };

    我们查看Vue相关的源码时可以发现为什么传统的错误捕获方式对Vue不适用,源码目录vue/src/core/util/error.js

    function globalHandleError (err, vm, info) {
      if (config.errorHandler) {
        try {
          return config.errorHandler.call(null, err, vm, info)
        } catch (e) {
          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
      }
    }

    从中我们可以看到没有被Vue捕获到的错误会以console.error(err)的方式抛出来,所以我们无论采用window.onerror还是采用window.addEventListener('error')的方式都是捕获不到任何错误的,但是Vue提供只能捕获其页面生命周期内的函数,比如created,mounted等等,从 2.4.0 起这个钩子也会捕获 Vue 自定义事件处理函数内部的错误了,所谓的自定义就是在生命周期钩子内调用的函数,如果一个函数不被生命周期钩子引用一旦报错,Vue是捕获不到的,这时就需要使用传统的错误捕获方式进行捕获了。

  2. 捕获网络请求相关错误

    由于网络请求异常不会事件冒泡,因此必须在捕获阶段将其捕捉才行,但是这种方式虽然可以捕捉到网络请求的异常,但是无法判断HTTP的转态是404,还是500d等等,所以要想具体查相关请求的HTTP状态还是需要配合服务端日志才行

    window.addEventListener('error', (msg, url, row, col, error) => {
        console.log('我知道错了')
        console.log(msg, url, row, col, error)
        return true
      }, true)

    我们可以通过这种错误捕获的方式捕获一些资源加载错误相关信息

  3. 捕获Promise错误

    一旦Promise实例抛出异常而你没有用catch去捕获的话,onerror或try-catch也无能为力,无法捕获到错误,这时我们可以采用

    window.addEventListener('unhandledrejection', function(e) {
        e.preventDefault()
        console.log('这是Promise的错误')
        console.log(e.reason)
        return true
      })

    进行promise相关错误的捕获

  4. 捕获ajax,axios,fetch等接口请求错误

    直接在请求发送期和响应期对数据进行劫持

    // 请求劫持
    axios.interceptors.request.use(config=> {
      return config;
    }, err=> {
      Message.error({message: '请求超时!'});
      return Promise.resolve(err);
    })
    // 响应劫持
    axios.interceptors.response.use(data=> {
      if (data.status && data.status == 200 && data.data.status == 'error') {
        Message.error({message: data.data.msg});
        return;
      }
      return data;
    }, err=> {
      if (err.response.status == 504||err.response.status == 404) {
        Message.error({message: '服务器被吃了⊙﹏⊙∥'});
      } else if (err.response.status == 403) {
        Message.error({message: '权限不足,请联系管理员!'});
      }else {
        Message.error({message: '未知错误!'});
      }
      return Promise.resolve(err);
    })

    然后进行收到收集上报到日志系统