前端监控项目捕获错误&上报

284 阅读2分钟

前端监控错误

背景:

对于项目上线后,很多时候有不可预估的错误,对于这些错误,我们不一定能准确还原,因此要检查并及时上报

  1. 首先,因为网络中断导致的字段缺失的报错我们可以优先过滤,监听用户网络连接,通过linkNet跳过上报
    let linkNet = false
    window.addEventListener('offline', () => {
        linkNet = true;
        toast.error('Network error!');
    });

  1. 监听JS异常错误,window.error 可以捕获常规错误/异步错误,但资源错误/语法错误无法捕获
     window.onerror = function (msg, url, row, col, error) {
        // 网络断开导致的错误我们其实都可以忽略 这里直接就return 下面同理
        if (linkNet) return;
        let errorMsg = {
            // msg错误消息,error是错误对象,这里拿的是error.stack(异常信息)
            msg: error && error.stack ? error.stack : msg,
            url,
            userID: user?.uuid || '未登录', // 报错用户ID
        };

        // 暂时没找到具体原因 先抛出
        if (msg === 'ResizeObserver loop limit exceeded') {
            return true;    // 如果return true,错误就不会抛到控制台
        }

        // deve测试环境不上报
        if (process.env.REACT_APP_ENV === 'development') return;
        // 上报错误信息服务
        pushError(errorMsg);

     
    };

  1. 监听资源加载错误,资源错误我们可以用过error事件监听捕获
     window.addEventListener('error', e => {
            if (linkNet) return;
            e.stopImmediatePropagation();
            const target = e.srcElement ? e.srcElement : e.target;

            //收集错误信息
            let errorMsg = {
                time: Date.now(),
                userID: user?.uuid || '未登录',
                msg: e.message || target.baseURI,
            };

            // 暂时没找到具体原因 先抛出
            if (e.message === 'ResizeObserver loop limit exceeded') {
                return;
            }

            if (target === window) {
                // 全局错误
                if (process.env.REACT_APP_ENV === 'development') return;
                
                pushError(errorMsg);

                // JS资源加载 (解决项目部署更新 资源文件丢失 自动刷新)
                if (e.message && String(e.message).includes('Loading chunk') && String(e.message).includes('failed')) {
                    window.location.replace(window.location.href);
                }

            } else {
                // 元素错误,比如引用资源报错
                if (process.env.REACT_APP_ENV === 'development') return;
                pushError(errorMsg);
            }
        }, true
    );
  1. 捕捉Promise错误,Promise抛出的错误可通过 unhandledrejection 事件来捕获处理
    window.addEventListener('unhandledrejection', e => {
        if (linkNet) return;
        let errorMsg = {
            url: e.target.baseURI,
            msg: e.reason.stack || e.reason,
            userID: user?.uuid || '未登录',
        };
        if (process.env.REACT_APP_ENV === 'development') return;
        pushError(errorMsg);

        // 如果return true,错误就不会抛到控制台
    });
  1. 当然你可以借助第三方成熟的监控工具Sentry 蒲公英等
补充vue、react框架也有自己的捕获机制
  1. react 通过错误边界ErrorBoundary捕获错误
class ErrorBoundary extends React.Component {
  constructor (props) {
    super(props)
    this.state = { hasError: false }
  }

  static getDerivedStateFromError (error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true }
  }
  componentDidCatch (error, errorInfo) {
    // componentDidCatch 可以捕获render函数的错误
    console.log(error, errorInfo)

    // 同样可以将错误日志上报给服务器
    reportError(error, errorInfo)
  }
  render () {
    if (this.state.hasError) {
      // 自定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>
    }
    return this.props.children
  }
}

function Parent () {
  return (
    <div>
      <ErrorBoundary>
        <div></div>
      </ErrorBoundary>
    </div>
  )
}
  1. vue 通过 Vue.config.errorHandler捕获
Vue.config.errorHandler = function (err, vm, info) {
 handleError方法用来处理错误并上报
    handleError(err);
}