最近在研究前端異常監控的問題,對查詢的資料作了整理彙總,整體以下html
window.onerror 不管是異步仍是非異步錯誤,onerror 都能捕獲到運行時錯誤。前端
window.onerror = function (msg, url, row, col, error) {
console.log('我知道錯誤了');
console.log({
msg, url, row, col, error
})
return true;
};
複製代碼
注意:webpack
window.addEventListener('error', (msg, url, row, col, error) => {
console.log('我知道錯誤了');
console.log(
msg, url, row, col, error
);
return true;
}, true);
複製代碼
Promise 實例拋出異常而你沒有用 catch 去捕獲的話,onerror 或 try-catch 也無能爲力,沒法捕捉到錯誤。 若是用到不少 Promise 實例的話,特別是你在一些基於 promise 的異步庫好比 axios 等必定要當心,由於你不知道何時這些異步請求會拋出異常而你並無處理它,因此你最好添加一個 Promise 全局異常捕獲事件 unhandledrejection。ios
window.addEventListener("unhandledrejection", function(e){
e.preventDefault()
console.log('我知道 promise 的錯誤了');
console.log(e.reason);
return true;
});
複製代碼
父窗口直接使用 window.onerror 是沒法直接捕獲,若是你想要捕獲 iframe 的異常的話,有分好幾種狀況。git
<iframe src="./iframe.html" frameborder="0"></iframe>
<script>
window.frames[0].onerror = function (msg, url, row, col, error) {
console.log('我知道 iframe 的錯誤了,也知道錯誤信息');
console.log({
msg, url, row, col, error
})
return true;
};
</script>
複製代碼
能夠經過與 iframe 通訊的方式將異常信息拋給主站接收。與 iframe 通訊的方式有不少,經常使用的如: postMessage,hash 或者 name字段跨域等等github
監控拿到報錯信息以後,接下來就須要將捕捉到的錯誤信息發送到信息收集平臺上,經常使用的發送形式主要有兩種:web
function error(msg,url,line){
var REPORT_URL = "xxxx/cgi"; // 收集上報數據的信息
var m =[msg, url, line, navigator.userAgent, +new Date];// 收集錯誤信息,發生錯誤的腳本文件網絡地址,用戶代理信息,時間
var url = REPORT_URL + m.join('||');// 組裝錯誤上報信息內容URL
var img = new Image;
img.onload = img.onerror = function(){
img = null;
};
img.src = url;// 發送數據到後臺cgi
}
// 監聽錯誤上報
window.onerror = function(msg,url,line){
error(msg,url,line);
}
複製代碼
該部分原文出處:https://github.com/joeyguo/blog/issues/14axios
示例:跨域
function test() {
noerror // <- 報錯
}
test();
複製代碼
!function(n){function r(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}var t={};r.m=n,r.c=t,r.i=function(n){return n},r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:e})},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,r){return Object.prototype.hasOwnProperty.call(n,r)},r.p="",r(r.s=0)}([function(n,r){function t(){noerror}t()}]);
複製代碼
{ msg: 'Uncaught ReferenceError: noerror is not defined',
url: 'http://127.0.0.1:8077/main.min.js',
row: '1',
col: '515' }
複製代碼
此時,錯誤信息中行列數爲 1 和 515。 結合壓縮後的代碼,肉眼觀察很難定位出具體問題。promise
uglifyjs 有一個叫 semicolons 配置參數,設置爲 false 時,會將壓縮代碼中的分號替換爲換行符,提升代碼可讀性, 如
!function(n){function r(e){if(t[e])return t[e].exports
var o=t[e]={i:e,l:!1,exports:{}}
return n[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}var t={}
r.m=n,r.c=t,r.i=function(n){return n},r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:e})},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n}
return r.d(t,"a",t),t},r.o=function(n,r){return Object.prototype.hasOwnProperty.call(n,r)},r.p="",r(r.s=0)}([function(n,r){function t(){noerror}t()}])
複製代碼
此時,錯誤信息中行列數爲 5 和 137,查找起來比普通壓縮方便很多。但仍會出現一行中有不少代碼,不容易定位的問題。
uglifyjs 的另外一配置參數 beautify 設置爲 true 時,最終代碼將呈現壓縮後進行格式化的效果(保留空格和換行),如
!function(n) {
// ...
// ...
}([ function(n, r) {
function t() {
noerror;
}
t();
} ]);
複製代碼
此時,錯誤信息中行列數爲 32 和 9,可以快速定位到具體位置,進而對應到源代碼。但因爲增長了換行和空格,因此文件大小有所增長。
SourceMap 是一個信息文件,存儲着源文件的信息及源文件與處理後文件的映射關係。 在定位壓縮代碼的報錯時,能夠經過錯誤信息的行列數與對應的 SourceMap 文件,處理後獲得源文件的具體錯誤信息。
SourceMap 文件中的 sourcesContent 字段對應源代碼內容,不但願將 SourceMap 文件發佈到外網上,而是將其存儲到腳本錯誤處理平臺上,只用在處理腳本錯誤中。 經過 SourceMap 文件能夠獲得源文件的具體錯誤信息,結合 sourcesContent 上源文件的內容進行可視化展現,讓報錯信息一目瞭然!
sentry 是一個實時的錯誤日誌追蹤和聚合平臺,包含了上面 sourcemap 方案,並支持更多功能,如:錯誤調用棧,log 信息,issue管理,多項目,多用戶,提供多種語言客戶端等,具體介紹能夠查看 getsentry/sentry,sentry.io,這裏暫不展開。