前端異常采集


為什么要做前端代碼異常采集?好問題!
為了用戶能安心用產品,不至於時不時“卡殼”崩潰。
為了能高效定位線上代碼的異常並提供簡單提示信息。
為了程序猿同胞們能睡個好覺。

本文完整示例請移步github:FEerrorLog

捕獲異常的方法

js捕獲異常的方法,兩三個而已。

  1. try...catch 優缺點已有很多論述和解決方案,本文的異常采集並未建立在該方法之上,只是少量使用。
  2. window.onerror和方法3類似但不如方法3強大,因此未選用此方法。
  3. window.addEventListener('error',function(){},true),采用此方法。

前端異常包含兩部分:
第一部分:window.onerror()能捕獲到的異常,當然如果用addEventListener無論冒泡還是捕獲階段也能捕獲到該異常。
第二部分:資源加載失敗,即<img><script>標簽上的onerror,這個異常無法通過冒泡到達window,但是可以在捕獲階段拿到,這就是為什么要將addEventListener第三個參數置成true了。

注意:為保證該異常采集腳本能執行到,不被先行執行的腳本里面的報錯阻斷,該腳本要放到最前面。

可能的異常及采集方案

  1. 資源加載失敗,樣式、圖片、腳本文件的請求異常,比如js加載404了
  2. js腳本異常,即控制台常見的Error信息
  3. 檢測HTML劫持,比如被運營商強行注入標簽或腳本
  4. 頁面樣式丟失,CSS 展現異常

1. 資源加載失敗

http異常用js幾乎抓不到有用異常信息,但是404異常可以進行簡單處理,此時是不會執行onerror的回調函數的。因為在addEventListener捕獲到的異常信息中你可以發現,對應於onerror的五個回調參數根本不存在了,但是addEventListener中除這五個外,還有其他可以用的key,如果想獲取加載失敗的資源是哪個,可以去target中找些有用信息,我使用的是e.target.outerHTML

"HttpError at " + (e.target.baseURI || location.href) + " outerHTML:" + e.target.outerHTML

2. js異常

一般需要采集的信息:

  1. 異常的提示信息,會直接告訴你是什么異常。這是識別一個異常的最重要依據,即e.message中的信息。
  2. JS 文件名:異常發生在那個文件中。是堆棧信息中最頂層的那個文件。即e.filename。
  3. 異常所在行、列:異常的具體位置。行信息各瀏覽器基本還是一致的,列信息的差別較大,僅供參考。
  4. 堆棧信息:異常信息發生的堆棧,也是函數調用的堆棧信息,每下一層都是上一層的運行環境。即e.error.stack。每一層都包含類型、文件、行、列信息。但是注意堆棧信息可能會比較多,可以根據需要截取上報。safari和firfox的e.error.stack中不包含以上1,2,3的信息,只有堆棧信息,而chrome和IE中都包含,此處需要做兼容處理。
  5. 發生異常的設備信息,可以從window.navigator中選取自己需要的信息,或者直接使用window.navigator.userAgent
  6. 發生異常的時間點,不多說。

js主動拋出異常
js異常除了可以是系統拋出的幾類異常,還可以是開發者利用throw關鍵字主動拋出異常。
值:可以是字符串、數字邏輯值或對象
使用方式:

//利用Error對象或其實例,采集到的異常系統自動為其添加了堆棧信息,和系統拋出異常基本類似
throw new Error('Problem description.')
throw Error('Problem description.') 
//直接利用關鍵字拋出內容,會完全復寫event.error的內容,不推薦使用。
throw 'Problem description.' 
throw null 

另外:
console.error()和throw new Error()拋出的錯誤信息是有本質區別的。前者不會阻斷js運行,也不會被error事件捕捉到,只是在控制台打印錯誤信息。

以下方式可以阻止異常信息在控制台中顯示,線上可以自行收集異常信息后阻止外人看到控制台報錯,開發環境不建議使用。

    window.addEventListener('error', (function(e) {
        console.log("-----errorEvent----", e)
        e.preventDefault()  //這里換成 return false或return true均不行!
    }), true);
    
    window.onerror = function(msg, url, line, col, error) {
        console.log("------errorInfo---",msg, url, line, col, error)
        return true;   //這里用return false不行!
    }

3. 檢測html劫持

我選用的方案是保存真實環境中的html信息,並對比原html,檢測是否有被篡改。
采集html文檔用到的是document.documentElement.outerHTML。但是有一點需要注意,上面已提到,該文件需要放在最前面,所以直接用該方法拿到的可能只有<head>中的html。因此如果想拿到完整的頁面信息,需要將采集時間點放到onload以后。

    //所有io操作最好都try...catch一下,這里是防止儲存的信息超過localStorage的最大限制。關於最大限制是多少已經有不少人說過了,大家可以選擇性看一些,如果有必要可以親測一下。
    window.addEventListener("load", function() {
        try {
            localStorage.setItem("hawkeyeHtml", document.documentElement.outerHTML);
        } catch (e) {
            //超過限度時,chrome和safari的e.name為'QuotaExceededError',FF的e.name為'NS_ERROR_DOM_QUOTA_REACHED'
            console.error(e)
        }
    })

4. 頁面樣式丟失

尚未做此數據采集和監控,目前考慮大體思路和html劫持類似,保存截屏圖片,用一定算法和正常樣式下做對比,超過一定差異值即判定為樣式異常。

數據處理方式:

  1. 上報監控服務器:便於統一監控異常數量類型等
    方案:我司仍選擇img的屬性src進行上報,一是考慮到解析性能,二是考慮到要為多站點服務,方便跨域上報。
  2. html頁面打印前端異常:便於快速准確定位某使用環境崩潰原因
    方案:采集到信息儲存在localstorage中,注意控制儲存的條數。在需要查看異常信息的網站中增加該頁面,取出進行簡單處理即可。

缺陷處理

  1. 線上代碼是混淆壓縮的,無行號
    解決方案:線上也用sourceMap來解決,至於sourMap是什么,怎么使用,根據項目不同,可自行google。
  2. 跨域的js,異常信息只有一個"Script error"。雖然js拿不到任何其他異常信息,但是控制台能打印出全部異常信息。所以
    解決方案一:匹配到"Script error",引導開者去控制台查看或直接過濾掉。
    解決方案二:解決跨域問題,分兩步。一:靜態資源請求需要加多一個Access-Control-Allow-Origin頭部。二:<scrip>標簽加上crossorigin屬性
  3. 跨域的js,獲取不到js文件名,會拿到at <anonymous>:1:1醬紫的信息,暫時沒找到解決方案

推薦文章:JavaScript Errors Handbook:里面有部分內容已不適用,具體實施請注意驗證


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM