前端數據監控到底在監控什麼?

前端數據監控通常分爲性能數據監控和線上異常監控。本文對這兩塊數據的監控原理和方法進行整理說明。php

性能數據

統計方案

  • 代碼監控
    • 將監控代碼注入到頁面中,手動計算時間差或者使用瀏覽器API進行數據統計。
  • 工具監控
    • 不將統計代碼注入到頁面中,通常藉助虛擬機對頁面進行性能數據分析。
類型 優勢 缺點 示例
非侵入式 指標齊全、客戶端主動監測、競品監控 沒法知道性能影響用戶數、採樣少容易失真、沒法監控複雜應用與細分功能 Pagespeed、PhantomJS、UAQ
侵入式 真實海量用戶數據、能監控複雜應用與業務功能、用戶點擊與區域渲染 需插入腳本統計、網絡指標不全、沒法監控競品 DP 、Google 統計

在進行性能數據監控以前,先要明確頁面從用戶開始訪問到頁面加載完成經歷的時間階段。css

時間階段

按觸發順序排列全部屬性:(更詳細標準的解釋請參看:W3C Editor's Draft)html

  • navigationStart:在同一個瀏覽器上下文中,前一個網頁(與當前頁面不必定同域)unload 的時間戳,若是無前一個網頁 unload ,則與 fetchStart 值相等前端

  • unloadEventStart:前一個網頁(與當前頁面同域)unload 的時間戳,若是無前一個網頁 unload 或者前一個網頁與當前頁面不一樣域,則值爲 0git

  • unloadEventEnd:和 unloadEventStart 相對應,返回前一個網頁 unload 事件綁定的回調函數執行完畢的時間戳github

  • redirectStart:第一個 HTTP 重定向發生時的時間。有跳轉且是同域名內的重定向纔算,不然值爲 0web

  • redirectEnd:最後一個 HTTP 重定向完成時的時間。有跳轉且是同域名內的重定向纔算,不然值爲 0chrome

  • fetchStart:瀏覽器準備好使用 HTTP 請求抓取文檔的時間,這發生在檢查本地緩存以前api

  • domainLookupStart:DNS 域名查詢開始的時間,若是使用了本地緩存(即無 DNS 查詢)或持久鏈接,則與 fetchStart 值相等跨域

  • domainLookupEnd:DNS 域名查詢完成的時間,若是使用了本地緩存(即無 DNS 查詢)或持久鏈接,則與 fetchStart 值相等

  • connectStart:HTTP(TCP) 開始創建鏈接的時間,若是是持久鏈接,則與 fetchStart 值相等,若是在傳輸層發生了錯誤且從新創建鏈接,則這裏顯示的是新創建的鏈接開始的時間

  • connectEnd:HTTP(TCP) 完成創建鏈接的時間(完成握手),若是是持久鏈接,則與 fetchStart 值相等,若是在傳輸層發生了錯誤且從新創建鏈接,則這裏顯示的是新創建的鏈接完成的時間

    注意:這裏握手結束,包括安全鏈接創建完成、SOCKS 受權經過

  • secureConnectionStart:HTTPS 鏈接開始的時間,若是不是安全鏈接,則值爲 0

  • requestStart:HTTP 請求讀取真實文檔開始的時間(完成創建鏈接),包括從本地讀取緩存,鏈接錯誤重連時,這裏顯示的也是新創建鏈接的時間

  • responseStart:HTTP 開始接收響應的時間(獲取到第一個字節),包括從本地讀取緩存

  • responseEnd:HTTP 響應所有接收完成的時間(獲取到最後一個字節),包括從本地讀取緩存

  • domLoading:開始解析渲染 DOM 樹的時間,此時 Document.readyState 變爲 loading,並將拋出 readystatechange 相關事件

  • domInteractive:完成解析 DOM 樹的時間,Document.readyState 變爲 interactive,並將拋出 readystatechange 相關事件

    注意:只是 DOM 樹解析完成,這時候並無開始加載網頁內的資源

  • domContentLoadedEventStart:DOM 解析完成後,網頁內資源加載開始的時間,文檔發生 DOMContentLoaded事件的時間

  • domContentLoadedEventEnd:DOM 解析完成後,網頁內資源加載完成的時間(如 JS 腳本加載執行完畢),文檔的DOMContentLoaded 事件的結束時間

  • domComplete:DOM 樹解析完成,且資源也準備就緒的時間,Document.readyState 變爲 complete,並將拋出 readystatechange 相關事件

  • loadEventStart:load 事件發送給文檔,也即 load 回調函數開始執行的時間,若是沒有綁定 load 事件,值爲 0

  • loadEventEnd:load 事件的回調函數執行完畢的時間,若是沒有綁定 load 事件,值爲 0

DOMContentLoaded 和 load 事件的區別,詳見 DOMContentLoaded與load的區別

統計方法

Navigation Timing API

能夠直接使用Navigation Timing接口來獲取統計起點以及加載過程當中的各個階段耗時。window.performance 是W3C性能小組引入的新的API,目前IE9以上的瀏覽器都支持。

經常使用計算:

  • DNS查詢耗時 :domainLookupEnd - domainLookupStart
  • TCP連接耗時 :connectEnd - connectStart
  • request請求耗時 :responseEnd - responseStart
  • 解析dom樹耗時 : domComplete - domInteractive 白屏時間 :responseStart - navigationStart
  • domready時間(用戶可操做時間節點) :domContentLoadedEventEnd - navigationStart
  • onload時間(總下載時間) :loadEventEnd - navigationStart

Resource timing API

Resource timing API是用來統計靜態資源相關的時間信息,詳細的內容請參考W3C Resource timing。這裏咱們只介紹performance.getEntries方法

指標及計算方法

指標

  • 整體指標
    • 到 DOM 可交互耗時timing.domComplete - timing.navigationStart
    • 總耗時timing.loadEventEnd - timing.navigationStart 到 DNS 查詢結束耗時timing.domainLookupEnd - timing.navigationStart
    • 到請求結束耗時timing.responseEnd - timing.navigationStart
    • 首次渲染耗時timing.msFirstPaint
  • 階段指標(按順序)
    • 重定向時間timing.redirectEnd - timing.redirectStart
    • unload 事件時間timing.unloadEventEnd - timing.unloadEventStart
    • appcache 時間timing.domainLookupStart - timing.fetchStart
    • DNS 查詢時間timing.domainLookupEnd - timing.domainLookupStart
    • 鏈接時間timing.connectEnd - timing.connectStart
    • 請求時間timing.responseEnd - timing.requestStart
    • 請求到 DOM 可交互時間(包含解析HTML,非defer的script和css的時間)timing.domInteractive - timing.responseEnd
    • DOM 可交互到 DOMReady 時間(包含處理defer的script的時間)timing.domComplete - * timing.domInteractive
    • load 事件時間timing.loadEventEnd - timing.loadEventStart

詳見源碼:timing

關鍵指標

  • 白屏時間(first paint time)- 用戶從打開頁面到頁面開始有內容呈現爲止
  • 首屏時間 - 用戶從打開頁面到首屏內全部內容都呈現出來所花費的時間
  • 用戶可操做時間(dom interactive) - 用戶從打開頁面到能夠進行正常點擊、輸入等操做的時間
  • 總下載時間 - 用戶從打開頁面到頁面全部資源都加載完畢並呈現出來所化的時間

肯定統計起點

咱們須要在用戶輸入 URL 或者點擊連接的時候就開始統計,由於這樣才能衡量用戶的等待時間。若是你的用戶高端瀏覽器佔比很高,那麼能夠直接使用Navigation Timing接口來獲取統計起點以及加載過程當中的各個階段耗時。

白屏時間

白屏時間是用戶首次看到內容的時間,也叫作首次渲染時間,chrome 高版本有 firstPaintTime 接口來獲取這個耗時,但大部分瀏覽器並不支持,必須想其餘辦法來監測。仔細觀察 WebPagetest 視圖分析發現,白屏時間出如今頭部外鏈資源加載完附近,由於瀏覽器只有加載並解析完頭部資源纔會真正渲染頁面。基於此咱們能夠經過獲取頭部資源加載完的時刻來近似統計白屏時間。儘管並不精確,但卻考慮了影響白屏的主要因素:首字節時間和頭部資源加載時間。

如何統計頭部資源加載呢?咱們發現頭部內嵌的 JS 一般需等待前面的 JS\CSS 加載完纔會執行,是否是能夠在瀏覽器 head 內底部加一句 JS 統計頭部資源加載結束點呢?能夠經過一個簡單的示例進行測試:

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="UTF-8"/>
    <script>
      var start_time = +new Date; //測試時間起點,實際統計起點爲 DNS 查詢
    </script>
    <!-- 3s 後這個 js 纔會返回 -->
    <script src="script.php"></script>  
    <script>
      var end_time = +new Date; //時間終點
      var headtime = end_time - start_time; //頭部資源加載時間    
      console.log(headtime);
    </script>
    </head> 
    <body>     
    <p>在頭部資源加載完以前頁面將是白屏</p>
    <p>script.php 被模擬設置 3s 後返回,head 底部內嵌 JS 等待前面 js 返回後才執行</p>
    <p>script.php 替換成一個執行長時間循環的 js 效果也同樣</p>  
    </body>
</html>
複製代碼

經測試發現,統計的頭部加載時間正好跟頭部資源下載時間相近,並且換成一個執行時間很長的 JS 也會等到 JS 執行完才統計。說明此方法是可行的(具體緣由可查看瀏覽器渲染原理及 JS 單線程相關介紹)。

除了上述方法,還能夠採用 navigation timing api 來獲取白屏時間。該方法的缺點是瀏覽器兼容性較差,在 safari 等瀏覽器中沒法使用。

var firstPaint = 0;

  // Chrome
  if (window.chrome && window.chrome.loadTimes) {
      // Convert to ms
      firstPaint = window.chrome.loadTimes().firstPaintTime * 1000;
      api.firstPaintTime = firstPaint - timing.navigationStart;
  }
  // IE
  else if (typeof timing.msFirstPaint === 'number') {
      firstPaint = timing.msFirstPaint;
      api.firstPaintTime = firstPaint - timing.navigationStart;
  }
  // Firefox
  // This will use the first times after MozAfterPaint fires
  //else if (timing.navigationStart && typeof InstallTrigger !== 'undefined') {
  //    api.firstPaint = timing.navigationStart;
  //    api.firstPaintTime = mozFirstPaintTime - timing.navigationStart;
  //}

複製代碼

首屏時間

在首屏渲染以前埋上處理邏輯,使用定時器不斷的去檢測img節點的圖片。判斷圖片是否在首屏和加載完成,找到首屏中加載時間最慢的的圖片完成的時間,從而計算出首屏時間。若是首屏有沒有圖片,若是沒圖片就用domready時間。統計流程以下:

首屏位置調用 API 開始統計 -> 綁定首屏內全部圖片的 load 事件 -> 頁面加載完後判斷圖片是否在首屏內,找出加載最慢的一張 -> 首屏時間
複製代碼

這是同步加載狀況下的簡單統計邏輯,另外須要注意的幾點:

  • 頁面存在 iframe 的狀況下也須要判斷加載時間
  • gif 圖片在 IE 上可能重複觸發 load 事件需排除
  • 異步渲染的狀況下應在異步獲取數據插入以後再計算首屏
  • css 重要背景圖片能夠經過 JS 請求圖片 url 來統計(瀏覽器不會重複加載)
  • 沒有圖片則以統計 JS 執行時間爲首屏,即認爲文字出現時間

統計用戶可操做

用戶可操做默承認以統計domready時間,由於一般會在這時候綁定事件操做。對於使用了模塊化異步加載的 JS 能夠在代碼中去主動標記重要 JS 的加載時間,這也是產品指標的統計方式。

performance.timing.domInteractive - performance.timing.navigationStart
複製代碼

總下載

總下載時間默承認以統計onload時間,這樣能夠統計同步加載的資源所有加載完的耗時。若是頁面中存在不少異步渲染,能夠將異步渲染所有完成的時間做爲總下載時間。

performance.timing.loadEventStart- performance.timing.navigationStart
複製代碼

線上異常

接口異常監控

方法很簡單,在接口出錯的狀況下主動打點上報錯誤。

JS 代碼異常監控

  • try catch 捕獲

這種方案要求開發人員在編寫代碼的時候,在預估有異常發生的代碼段使用try...catch,在發生異常時將異常信息發送給接口:

try{
//可能發生異常的代碼段
}catch(e){
//將異常信息發送服務端
}
複製代碼
  • window.onerror捕獲

這種方式不須要開發人員在代碼中書寫大量的try...catch,經過給window添加onerror監聽,在js發生異常的時候即可以捕獲到錯誤信息,語法異常和運行異常都可被捕獲到。可是window.onerror這個監聽必須放在全部js文件以前才能夠保證可以捕獲到全部的異常信息。

  • 跨域JS文件異常的捕獲

目前能夠說基本上全部的web產品對於js/css/image等靜態資源都在服務端設置了Access-Control-Allow-Origin: *的響應頭,也就是容許跨域請求。在這個環境下,只要咱們在請求跨域資源的script標籤上添加一個crossorigin屬性便可:

<script src="http://static.toutiao.com/test.js" crossorigin></script>
複製代碼

這樣的話,異域的test.js文件中發生異常時即可以被當前域的onerror監聽捕獲到詳細的異常信息。

參考資料

  1. 前端性能 -- 監控起步
  2. Performance — 前端性能監控利器
  3. 前端性能監控方案window.performance 調研(轉)
  4. 研究首屏時間?你先要知道這幾點細節
  5. 初探 performance – 監控網頁與程序性能
  6. 7 天打造前端性能監控系統
  7. DOMContentLoaded與load的區別
相關文章
相關標籤/搜索