前言
公司的項目上線出現問題后難以定位錯誤,研究過現存的錯誤監控方案,受限于特殊條件只能定制自己的錯誤收集方案。
基于以上背景我擼出來一個錯誤日志收集方案 - 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);
復制代碼
window.addEventListener('error', (e) => {console.log(e);
}, true);
復制代碼
除此之外,addEventListener
還可以捕獲資源加載錯誤、未 catch 的 promise 錯誤。
// 捕獲未 catch 的 promise 錯誤
window.addEventListener("unhandledrejection", e => {e.preventDefault();console.log(e);
});
Promise.reject('promiseError');
復制代碼
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 = {// 記錄請求的 urlreqUrl: '',// 記錄請求的方法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 監控
- 前端一站式異常捕獲方案(全)