前端监控错误
背景:
对于项目上线后,很多时候有不可预估的错误,对于这些错误,我们不一定能准确还原,因此要检查并及时上报
- 首先,因为网络中断导致的字段缺失的报错我们可以优先过滤,监听用户网络连接,通过linkNet跳过上报
let linkNet = false
window.addEventListener('offline', () => {
linkNet = true;
toast.error('Network error!');
});
- 监听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);
};
- 监听资源加载错误,资源错误我们可以用过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
);
- 捕捉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,错误就不会抛到控制台
});
- 当然你可以借助第三方成熟的监控工具Sentry 蒲公英等
补充vue、react框架也有自己的捕获机制
- 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>
)
}
- vue 通过 Vue.config.errorHandler捕获
Vue.config.errorHandler = function (err, vm, info) {
handleError方法用来处理错误并上报
handleError(err);
}