FE.B-異常監控原理
js錯誤主要有2類:語法錯誤
、指令碼錯誤
;
監控方式有2種:try-catch
、window.onerror
try-catch 異常處理
try-catch
處理異常的能力有限,只能捕獲到執行時的非非同步錯誤
,對於語法錯誤和非同步錯誤就顯得無能為力。
try{ error // <- 未定義變數 } catch(e) { console.log('捕獲到錯誤'); console.log(e); } //輸出: //ReferenceError: error is not defined
try { var error = 'error'; // <-大寫分號 } catch(e) { console.log('捕獲不到錯誤'); console.log(e); } //輸出: //Uncaught SyntaxError: Invalid or unexpected token
try{ setTimeout(function () { error // <- 非同步錯誤 }, 0) } catch(e) { console.log('捕獲不到錯誤'); console.log(e); } //輸出: //Uncaught ReferenceError: error is not defined
window.onerror 異常處理
window.onerror
捕獲異常的能力比try-catch
稍強一點,無論是非同步還是非非同步的錯誤,onerror
都能捕獲到執行時的錯誤
/** * @param {String}msg錯誤資訊 * @param {String}url出錯檔案 * @param {Number}row行號 * @param {Number}col列號 * @param {Object}error錯誤詳細資訊 */ window.onerror = function (msg, url, row, col, error) { console.log('捕獲到執行時同步錯誤了'); console.log({ msg,url,row, col, error }) return true; }; error; // <- 未定義變數 //輸出: //捕獲到錯誤了 //{msg: "Uncaught ReferenceError: error is not defined", ...}
window.onerror = function (msg, url, row, col, error) { console.log('捕獲到非同步錯誤了'); console.log({ msg,url,row, col, error }) return true; }; setTimeout(() => { error; // <- 未定義變數 }); //輸出: //捕獲到非同步錯誤了 //{msg: "Uncaught ReferenceError: error is not defined", ...}
在實際使用中,onerror
主要用來捕獲預料之外的錯誤,try-catch
則是用來在可預見情況下監控特定的錯誤,兩者結合使用更加高效
但是對於語法錯誤,window.onerror
還是捕獲不了,所以我們在寫程式碼的時候要儘可能避免語法錯誤,不過一般這種錯誤比較容易察覺。
除了語法錯誤不能捕獲之外,網路異常的錯誤也是不能捕獲的
<script> window.onerror = function (msg, url, row, col, error) { console.log('我知道錯誤了'); console.log({ msg,url,row, col, error }) return true; }; </script> <img src="./404.jpg"/> 輸出: GET http://localhost:8081/404.jpg 404 (Not Found)
這是因為網路請求是沒有事件冒泡的,所以需要在捕獲階段才能捕獲到異常,雖然這樣可以捕獲到網路的異常,但無法判斷http的狀態,比如該異常是404還是500,想要知道這個狀態就必須和服務日誌一起排查了。
<script> window.addEventListener('error', (msg, url, row, col, error) => { console.log('我知道錯誤了'); console.log({ msg,url,row, col, error }) return true; }, true); </script> <img src="./404.jpg"/> 輸出: GET http://localhost:8081/404.jpg 404 (Not Found) 我知道錯誤了 {msg: Event, url: undefined, row: undefined, col: undefined, error: undefined}
Promise
的錯誤沒有使用catch
去捕獲的話,上述的方式都是不能捕獲到錯誤的。但通過監聽unhandledrejection
事件,可以捕獲未處理的Promise錯誤。但是需要注意的是,這個事件是有相容問題的。
window.addEventListener("unhandledrejection", function(e){ e.preventDefault() console.log('我知道 promise 的錯誤了'); console.log(e.reason); return true; }); new Promise((resolve, reject) => { reject('promise error'); }); 輸出: 我知道 promise 的錯誤了 promise error
說完這些捕獲異常的方式之後,該說說異常上報的常用方法了。
異常上報
當我們拿到報錯資訊之後,就需要上報這些異常資訊,我們上報的方式通常有兩種方法:
Ajax
function report(error) { var reportUrl = 'http://xxxx/report'; new Image().src = reportUrl + 'error=' + error; }
script error 指令碼錯誤
在一個域下引用了其他域的指令碼,又沒有去做額外的配資,就很容易產生Script error
。說到最後這就是因為瀏覽器的同源策略產生的。所以最好我們還是使用跨源資源共享機制( CORS )
// http://localhost:8080/index.html <script> window.onerror = function (msg, url, row, col, error) { console.log('我知道錯誤了,也知道錯誤資訊'); console.log({ msg,url,row, col, error }) return true; }; </script> <script src="http://localhost:8081/test.js" crossorigin></script> // http://localhost:8081/test.js setTimeout(() => { console.log(error); });