前端異常監控-看這篇就夠了

前端異常監控

若是debug是移除bug的流程,那麼編程就必定是將bug放進去的流程。
若是沒有用戶反饋問題,那就表明咱們的產品棒棒噠,對不對?

主要內容

  • Web規範中相關前端異常
  • 異常按照捕獲方式分類
  • 異常的捕獲方式
  • 日誌上報的方式

前端異常類型(Execption)

WebIDLecma-262中的錯誤類型

  • ECMAScript exceptions <==> IDL 的簡單異常前端

    當腳本代碼運行時發生的錯誤,會建立Error對象,並將其拋出,除了通用的Error構造函數外,如下是另外幾個ECMAScript 2015中定義的錯誤構造函數。git

    • EvalError eval錯誤
    • RangeError 範圍錯誤
    • ReferenceError 引用錯誤
    • TypeError 類型錯誤
    • URIError URI錯誤
    • SyntaxError 語法錯誤 (這個錯誤WebIDL中故意省略,保留給ES解析器使用)
    • Error 通用錯誤 (這個錯誤WebIDL中故意省略,保留給開發者使用使用)
  • DOMException 最新的DOM規範定義的錯誤類型集,兼容舊瀏覽的DOMError接口, 完善和規範化DOM錯誤類型。github

    • IndexSizeError 索引不在容許的範圍內
    • HierarchyRequestError 節點樹層次結構是不正確的。
    • WrongDocumentError 對象是錯誤的
    • InvalidCharacterError 字符串包含無效字符。
    • NoModificationAllowedError 對象不能被修改。
    • NotFoundError 對象不能在這裏被找到。
    • NotSupportedError 不支持的操做
    • InvalidStateError 對象是一個無效的狀態。
    • SyntaxError 字符串不匹配預期的模式
    • InvalidModificationError 對象不能以這種方式被修改
    • NamespaceError 操做在XML命名空間內是不被容許的
    • InvalidAccessError 對象不支持這種操做或參數。
    • TypeMismatchError 對象的類型不匹配預期的類型。
    • SecurityError 此操做是不安全的。
    • NetworkError 發生網絡錯誤
    • AbortError 操做被停止
    • URLMismatchError 給定的URL不匹配另外一個URL。
    • QuotaExceededError 已經超過給定配額。
    • TimeoutError 操做超時。
    • InvalidNodeTypeError 這個操做的 節點或節點祖先 是不正確的
    • DataCloneError 對象不能克隆。

前端錯誤異常按照捕獲方式分類

  • [x] 語法錯誤
  • [x] 運行時異常
  • [x] 資源加載異常web

    • img
    • script
    • link
    • audio
    • video
    • iframe
    • ...外鏈資源的DOM元素
  • [x] 異步請求異常編程

    • XMLHttpRequest
    • fetch
  • [x] Promise異常
  • [ ] CSS中資源異常segmentfault

    • @font-face
    • background-image
    • ...暫時沒法捕獲

前端錯誤異常的捕獲方式

  • try-catch (ES提供基本的錯誤捕獲語法)後端

    • 只能捕獲同步代碼的異常
    • 回調
    • setTimeout
    • promise
  • window.onerror = cb (DOM0)跨域

    • img
    • script
    • link
  • window.addEventListener('error', cb, true) (DOM2)
  • window.addEventListener("unhandledrejection", cb) (DOM4)
  • Promise.then().catch(cb)
  • 封裝XMLHttpRequest&fetch | 覆寫請求接口對象

try-catch-finally

將能引起異常的代碼塊放到try中,並對應一個響應,而後有異常會被捕獲promise

try {
    // 模擬一段可能有錯誤的代碼
    throw new Error("會有錯誤的代碼塊")
  } catch(e){
    // 捕獲到try中代碼塊的錯誤獲得一個錯誤對象e,進行處理分析
    report(e)
  } finally {
    console.log("finally")
  }

onerror事件

window.onerror

當JavaScript運行時錯誤(包括語法錯誤)發生時,window會觸發一個ErrorEvent接口的事件,並執行window.onerror();瀏覽器

但這裏有個信息要注意,語法錯誤會致使出現語法錯誤的那個腳本塊執行失敗,因此語法錯誤會致使當前代碼塊運行終止,從而致使整個程序運行中斷,若是語法錯誤這個發生在咱們的錯誤監控語句塊中,那麼咱們就什麼也監控不到了。

/**
 * @description 運行時錯誤處理器
 * @param {string} message 錯誤信息
 * @param {string} source 發生錯誤的腳本URL
 * @param {number} lineno 發生錯誤的行號
 * @param {number} colno 發生錯誤的列號
 * @param {object} error Error對象
 */
function err(message,source,lineno,colno,error) {...}
window.onerror = err

element.onerror

當一項資源(如<img><script>)加載失敗,加載資源的元素會觸發一個Event接口的error事件,並執行該元素上的onerror()處理函數。

element.onerror = function(event) { ... } //注意和window.onerror的參數不一樣

注意:這些error事件不會向上冒泡到window,不過能被單一的window.addEventListener捕獲。

window.addEventListener

addEventListener相關的一些內容

W3C DOM2 Events規範中提供的註冊事件監聽器的方法, 在這以前均使用
el.onclick的形式(DOM0 規範的基本內容,幾乎全部瀏覽器都支持)。

注意: 接口的幾種語法

error事件捕獲資源加載錯誤

資源加載失敗,不會冒泡,可是會被addEventListener捕獲,因此咱們能夠指定在加載失敗事件的捕獲階段捕獲該錯誤。

注意: 接口同時也能捕獲運行時錯誤。

window.addEventListener("error", function(e) {
    var eventType = [].toString.call(e, e);
    if (eventType === "[object Event]") { // 過濾掉運行時錯誤
      // 上報加載錯誤
      report(e)
    }
  },
  true
);

unhandledrejection事件捕獲Promise異常

最新的規範中定義了 unhandledrejection事件用於全局捕獲promise對象沒有rejection處理器時異常狀況。

window.addEventListener("unhandledrejection", function (event) {
    // ...your code here to handle the unhandled rejection...

    // Prevent the default handling (error in console)
    event.preventDefault();
});

Promise.then().catch(cb).finally()

Promise中的錯誤會被Promise.prototype.catch捕獲,因此咱們經過這種方式捕獲錯誤,這包括一些不支持unhandledrejection事件的環境中promisede polyfill實現。

new Promise(function(resolve, reject) {
  throw 'Uncaught Exception!';
}).catch(function(e) {
  console.log(e); // Uncaught Exception!
});

封裝XMLHttpRequest&fetch | 覆寫請求接口對象

// 覆寫XMLHttpRequest API
if(!window.XMLHttpRequest) return;
  var xmlhttp = window.XMLHttpRequest;
  var _oldSend = xmlhttp.prototype.send;
  var _handleEvent = function (event) {
      if (event && event.currentTarget && event.currentTarget.status !== 200) {
        report(event)
      }
  }
  xmlhttp.prototype.send = function () {
      if (this['addEventListener']) {
          this['addEventListener']('error', _handleEvent);
          this['addEventListener']('load', _handleEvent);
          this['addEventListener']('abort', _handleEvent);
          this['addEventListener']('close', _handleEvent);
      } else {
          var _oldStateChange = this['onreadystatechange'];
          this['onreadystatechange'] = function (event) {
              if (this.readyState === 4) {
                  _handleEvent(event);
              }
              _oldStateChange && _oldStateChange.apply(this, arguments);
          };
      }
      return _oldSend.apply(this, arguments);
  }

// 覆寫fetch API
if (!window.fetch) return;
var _oldFetch = window.fetch;
window.fetch = function() {
  return _oldFetch
    .apply(this, arguments)
    .then(function(res){
      if (!res.ok) {
        // True if status is HTTP 2xx
        report(res)
      }
      return res;
    })
    .catch(function(error){
      report(res)
    });
}

日誌上報的方式

  • 異步請求上報, 後端提供接口,或者直接發到日誌服務器
  • img請求上報, url參數帶上錯誤信息

    • eg:(new Image()).src = 'http://baidu.com/tesjk?r=tksjk'

注意跨源腳本異常

當加載自不一樣域的腳本中發生語法錯誤時,爲避免信息泄露,語法錯誤的細節將不會報告,而代之簡單的 "Script error."

因爲同源策略影響,瀏覽器限制跨源腳本的錯誤訪問,這樣跨源腳本錯誤報錯信息以下圖:

跨源的腳本的異常

在H5的規定中,只要知足下面倆個條件,是容許獲取跨源腳本的錯誤信息的。

  1. 客戶端在script標籤上增長crossorigin屬性;
  2. 服務端設置js資源響應頭Access-Control-Origin:*(或者是域名)。

擴展閱讀

業界已經有的監控平臺

  • Sentry開源
  • 阿里的ARMS
  • fundebug
  • FrontJS

幾個異常監控的問題

  • 如何保證你們提交的代碼是符合預期的? 如何瞭解前端項目的運行是否正常,是否存在錯誤?

    代碼質量體系控制和錯誤監控以及性能分析

  • 若是用戶使用網頁,發現白屏,如今聯繫上了大家,大家會向他詢問什麼信息呢?先想一下爲何會白屏?

    咱們以用戶訪問頁面的過程爲順序,大體排查一下

    1. 用戶沒打開網絡
    2. DNS域名劫持
    3. http劫持
    4. cdn或是其餘資源文件訪問出錯
    5. 服務器錯誤
    6. 前端代碼錯誤
    7. 前端兼容性問題
    8. 用戶操做出錯

經過以上可能發生錯誤的環節,咱們須要向用戶手機一下如下的用戶信息

    1. 當前的網絡狀態
    2. 運營商
    3. 地理位置
    4. 訪問時間
    5. 客戶端的版本(若是是經過客戶端訪問)
    6. 系統版本
    7. 瀏覽器信息
    8. 設備分辨率
    9. 頁面的來源
    10. 用戶的帳號信息
    11. 經過performance API收集用戶各個頁面訪問流程所消耗的時間
    12. 收集用戶js代碼報錯的信息
    • 若是咱們使用了腳本代碼壓縮,然而咱們又不想將sourcemap文件發佈到線上,咱們怎麼捕獲到錯誤的具體信息?
    • CSS文件中也存在引用資源,@font-face, background-image ...等這些請求錯誤該如何進行錯誤捕獲?

    總結

    • Web規範中相關前端異常

      • DOM處理異常
      • ECMAScript處理異常
    • 異常按照捕獲方式分類

      • 運行時異常
      • 資源加載異常
      • 異步請求異常
      • Promise異常
    • 異常的捕獲方式

      • try-catch (ES提供基本的錯誤捕獲語法)

        • 只能捕獲同步代碼的異常
        • 回調
        • setTimeout
        • promise
      • window.onerror = cb (DOM0)

        • img
        • script
        • link
      • window.addEventListener('error', cb, true) (DOM2)
      • window.addEventListener("unhandledrejection", cb) (DOM4)
      • Promise.then().catch(cb)
      • 封裝XMLHttpRequest&fetch | 覆寫請求接口對象

    注意點:跨源腳本異常的捕獲

    • 日誌上報的方式

      • 異步請求上報
      • new img上報 避免跨域問題
    • 擴展閱讀

      • 業界已有的異常監控平臺
      • 幾個跟異常監控有關的問題

    ==========12月13日修正===========語法錯誤的捕獲有些特殊,通常狀況下,語法錯誤在開發階段就會報錯,很容易解決。可是若是在上線以後程序運行在不兼容的環境中也可能存在語法錯誤,引用的外部腳本存在語法錯誤等狀況,咱們就能夠捕獲到一個包含錯誤信息的錯誤對象,而不只僅是「Uncaught SyntaxError: Invalid or unexpected token」

    相關文章
    相關標籤/搜索