公司的項目上線出現問題後難以定位錯誤,研究過現存的錯誤監控方案,受限於特殊條件只能定製本身的錯誤收集方案。javascript
基於以上背景我擼出來一個錯誤日誌收集方案 - Ohbug。php
歡迎各位大佬 star ~css
提及錯誤的捕獲,首先想到的是 try catch
,經過 catch
捕獲到錯誤後進一步作出處理html
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
,因此可否捕獲全局的錯誤呢?vue
React 16
提供了一個內置函數 componentDidCatch
,使用它能夠很是簡單的獲取到 react 下的錯誤信息java
componentDidCatch(error, info) {
console.log(error, info);
}
複製代碼
React 16 的異常/錯誤處理react
指定組件的渲染和觀察期間未捕獲錯誤的處理函數。這個處理函數被調用時,可獲取錯誤信息和 Vue 實例。ios
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` 是 Vue 特定的錯誤信息,好比錯誤所在的生命週期鉤子
// 只在 2.2.0+ 可用
}
複製代碼
errorHandlerc++
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');
複製代碼
想要監控請求失敗,上面的方法確定是不可取的了。
使用 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
對象的方法實現對網絡請求的監控。
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();
複製代碼
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();
複製代碼