前言
公司的项目上线出现问题后难以定位错误,研究过现存的错误监控方案,受限于特殊条件只能定制自己的错误收集方案。
基于以上背景我撸出来一个错误日志收集方案 - Ohbug。
欢迎各位大佬 star ~
监控错误
说起错误的捕获,首先想到的是 try catch
,通过 catch
捕获到错误后进一步做出处理
try {
undefined.map(v => v);
} catch(e) {
console.log(e); // TypeError: Cannot read property 'map' of undefined
}
然而 try catch
对于异步产生的错误毫无感知
try {
setTimeout(() => {
undefined.map(v => v);
}, 1000)
} catch(e) {
console.log(e); // TypeError: Cannot read property 'map' of undefined
}
并且在实际工作中我也不可能给所有代码加上 try catch
,所以能否捕获全局的错误呢?
react componentDidCatch
React 16
提供了一个内置函数 componentDidCatch
,使用它可以非常简单的获取到 react 下的错误信息
componentDidCatch(error, info) {
console.log(error, info);
}
React 16 的异常/错误处理
vue errorHandler
指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
// 只在 2.2.0+ 可用
}
errorHandler
onerror
vs addEventListener
对于没有使用 react 或 vue 的项目可以通过 onerror
或 addEventListener
监控全局错误(当然使用 react 或 vue 的项目同样可以)
onerror
或 addEventListener
都可以捕获到一些未知的错误,然而这两个有什么区别呢?
window.onerror = (msg, url, row, col, error) => {
console.log({msg, url, row, col, error});
};
setTimeout(() => {
undefined.map(v => v);
}, 1000);
image
window.addEventListener('error', (e) => {
console.log(e);
}, true);
image
除此之外,addEventListener
还可以捕获资源加载错误、未 catch 的 promise 错误。
image
// 捕获未 catch 的 promise 错误
window.addEventListener("unhandledrejection", e => {
e.preventDefault();
console.log(e);
});
Promise.reject('promiseError');
image
ajax/fetch 错误监控
想要监控请求失败,上面的方法肯定是不可取的了。
使用 axios
的小伙伴可以通过配置拦截器实现错误的监控。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
这里我采用了重新封装 XMLHttpRequest
/fetch
对象的方法实现对网络请求的监控。
XMLHttpRequest
const AJAX = {
// 记录请求的 url
reqUrl: '',
// 记录请求的方法
reqMethod: '',
// 保存原生的 open 方法
xhrOpen: window.XMLHttpRequest.prototype.open,
// 保存原生的 send 方法
xhrSend: window.XMLHttpRequest.prototype.send,
init() {
const that = this;
window.XMLHttpRequest.prototype.open = function () {
that.reqUrl = arguments[1];
that.reqMethod = arguments[0];
that.xhrOpen.apply(this, arguments);
};
window.XMLHttpRequest.prototype.send = function () {
this.addEventListener('readystatechange', function () {
if (this.readyState === 4) {
if (!this.status || this.status >= 400) {
// 错误收集
}
}
});
that.xhrSend.apply(this, arguments);
};
},
};
AJAX.init();
fetch
const FETCH = {
backup: window.fetch,
init() {
window.fetch = function (url, conf) {
return (
FETCH.backup.apply(this, arguments)
.then((res) => {
if (!res.status || res.status >= 400) {
// 错误收集
}
return res;
})
);
};
},
};
FETCH.init();
待实现功能
- 捕获websocket错误
- 设置采集率
- sourcemap定位压缩代码具体错误位置
参考文章
- 前端代码异常监控实战
- Js error 监控
- 前端一站式异常捕获方案(全)