前端错误监控

目录

js运行时错误

普通错误

错误捕获

捕获到的错误信息

未捕获的Promise错误

错误捕获方式

更推荐的做法

关于Vue 的 errorHandler

关于 React 的 ErrorBoundary

网络请求错误

静态资源加载失败

如何监控静态资源加载失败呢?

 Script error,拿不到错误信息?

捕获AJAX 错误

捕获 fetch 错误


前端错误监控大分为两大类:js运行时错误,网络请求错误

js运行时错误可分为:普通错误,未捕获的Promise错误

网络请求错误可分为:静态资源加载失败,AJAX (XHR,fetch)请求失败

js运行时错误

普通错误

比如我们调用了一个不存在函数

错误捕获

    window.addEventListener('error', function (e) {
      console.log('error1:', e)
      // 上报
      // monitorSDK.report(你要上报的信息)
    })
    window.onerror = function (e) {
      console.log('error2:', e)
    }

    xxx();

捕获到的错误信息

未捕获的Promise错误

可以看到,如果一个Promise发生错误,但是没有用catch进行处理,将会抛出一个错误,这种错误如何捕获呢?用第1种的方式可行吗?这里新增一个错误捕获方式

    window.addEventListener('error', function (e) {
      console.log('error1:', e)
    })
    window.onerror = function (e) {
      console.log('error2:', e)
    }

    window.addEventListener('unhandledrejection',function(e){
      console.log('unhandledreject:', e)
    })

    new Promise(()=>xxx);

错误捕获方式

可以看到,第1种并不能捕获Promise错误,而是需要用  window.addEventListener('unhandledrejection') 来捕获

更推荐的做法

为了使代码更加健壮,建议 catch 所有的Promise错误

关于Vue 的 errorHandler

Vue为了防止应用崩溃,内部进行了 try catch ,导致没有错误抛出,我们可以通过在 errorHandler 里拿到错误信息,并主动上报

关于 React 的 ErrorBoundary

React 针对应用渲染时发生错误的处理方式和Vue不一样,如果渲染发生错误并且没有用ErrorBounbary包裹,整个应用将崩溃,React认为不显示比显示一个错误的UI更合理,当然了这个没有说绝对的谁好谁坏。
 

class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
  static getDerivedStateFromError(e: any) {
    console.error('getDerivedStateFromError: ', e);
    // 这里可以做错误上报
    return { hasError: true };
  }

  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false };
  }

  render() {
    if (this.state.hasError) {
      return '这是错误发生时的UI';
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

网络请求错误

静态资源加载失败

静态资源包括js,css,image,字体文件等

如何监控静态资源加载失败呢?

<!DOCTYPE html>
<html lang="en">

<head>
  <script>
    window.addEventListener('error', function (e) {
      console.log('error1:', e)
    })
  </script>

</head>

<body>
  <img id="image" src="./404.png" alt="">
  <script>
    document.getElementsByTagName('img')[0]
      .addEventListener('error', function (e) {
        console.log('image error', e)
      })
  </script>
</body>

</html>

输入结果

可以看到, window.addEventListener('error')并没有捕获到错误,这是为什么?
原来静态资源的错误,并不会冒泡到window,只能在事件捕获阶段拿到,调整代码,第3个参数改为true

    window.addEventListener('error', function (e) {
      console.log('error1:', e)
    },true)

成功拿到错误

 Script error,拿不到错误信息?

Script error. 也被称为跨域错误,当网站请求并且执行一个非本域名下的脚本的时候,如果跨域脚本发生错误,就有可能抛出这个错误。由于项目中,我们的脚本都是放在 CDN 上的,因此这种错误最为常见。

出于安全考虑,浏览器会刻意隐藏其他域的JS文件抛出的具体错误信息,这样做可以有效避免敏感信息无意中被不受控制的第三方脚本捕获。因此,浏览器只允许同域下的脚本捕获具体错误信息,而其他脚本只知道发生了一个错误,但无法获知错误的具体内容
可以参考 Script error · Issue #3 · BetterJS/badjs-report · GitHub


捕获AJAX 错误

不论是XHR还是fetch,我们都是通过函数劫持的办法实现错误监控的

XHR

const trackXHR = () => {
  const XHR = window.XMLHttpRequest;
  const originSend = XHR.prototype.send;
  XHR.prototype.send = function () {
    const errorHandler = (event) => {
      try {
        if (event && event.currentTarget && event.currentTarget.status !== 200) {
          const log = {
            status: this.status,
            type: 'xhr',
            // 其他要上报的数据
          }
          // 这里上报
          console.log('log----', log)
        }
      } catch {
        // SDK error
      }
    }
    // 重写load、error、abort方法,来处理请求结束时的逻辑,但是为什么不用函数拦截呢?
    // 因为用户有时候可能不会去调用 xhr.onload、xhr.error、xhr.abort 方法,如果都没有调用,拦截函数也不会执行,
    // 所以这里可以用 addEventListener 的方式
    this.addEventListener('load', errorHandler, false)
    this.addEventListener('error', errorHandler, false)
    this.addEventListener('abort', errorHandler, false)
    return originSend.apply(this, arguments)
  }

  // 兼容一些特殊场景,有些场景 XHR 对象不存在responseURL属性,这里记录下来
  const originOpen = XHR.prototype.open
  XHR.prototype.open = function (methods, url, async) {
    this.ajaxURL = url;
    return originOpen.apply(this, arguments)
  }
}

捕获 fetch 错误

const trackFetch = () => {
  if (!window.fetch) return;
  const originFetch = window.fetch;
  window.fetch = function () {
    return originFetch.apply(this, arguments)
      .then(res => {
        if (res.ok) {
          return res;
        }
        // 响应不为 ok 时,比如接口 400 404,500,上报错误信息
      }).catch(err=>{
        // 如果代码有bug,或者请求发生了跨域错误,则会执行这里
        // 在这里上报错误信息

        // 并且抛出错误,让错误能被其他地方捕获到
        throw err 
      })
  }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值