前端數據監控通常分爲性能數據監控和線上異常監控。本文對這兩塊數據的監控原理和方法進行整理說明。php
類型 | 優勢 | 缺點 | 示例 |
---|---|---|---|
非侵入式 | 指標齊全、客戶端主動監測、競品監控 | 沒法知道性能影響用戶數、採樣少容易失真、沒法監控複雜應用與細分功能 | 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接口來獲取統計起點以及加載過程當中的各個階段耗時。window.performance 是W3C性能小組引入的新的API,目前IE9以上的瀏覽器都支持。
經常使用計算:
Resource timing API是用來統計靜態資源相關的時間信息,詳細的內容請參考W3C Resource timing。這裏咱們只介紹performance.getEntries方法
詳見源碼:timing
咱們須要在用戶輸入 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 事件 -> 頁面加載完後判斷圖片是否在首屏內,找出加載最慢的一張 -> 首屏時間
複製代碼
這是同步加載狀況下的簡單統計邏輯,另外須要注意的幾點:
用戶可操做默承認以統計domready時間,由於一般會在這時候綁定事件操做。對於使用了模塊化異步加載的 JS 能夠在代碼中去主動標記重要 JS 的加載時間,這也是產品指標的統計方式。
performance.timing.domInteractive - performance.timing.navigationStart
複製代碼
總下載時間默承認以統計onload時間,這樣能夠統計同步加載的資源所有加載完的耗時。若是頁面中存在不少異步渲染,能夠將異步渲染所有完成的時間做爲總下載時間。
performance.timing.loadEventStart- performance.timing.navigationStart
複製代碼
方法很簡單,在接口出錯的狀況下主動打點上報錯誤。
這種方案要求開發人員在編寫代碼的時候,在預估有異常發生的代碼段使用try...catch,在發生異常時將異常信息發送給接口:
try{
//可能發生異常的代碼段
}catch(e){
//將異常信息發送服務端
}
複製代碼
這種方式不須要開發人員在代碼中書寫大量的try...catch,經過給window添加onerror監聽,在js發生異常的時候即可以捕獲到錯誤信息,語法異常和運行異常都可被捕獲到。可是window.onerror這個監聽必須放在全部js文件以前才能夠保證可以捕獲到全部的異常信息。
目前能夠說基本上全部的web產品對於js/css/image等靜態資源都在服務端設置了Access-Control-Allow-Origin: *的響應頭,也就是容許跨域請求。在這個環境下,只要咱們在請求跨域資源的script標籤上添加一個crossorigin屬性便可:
<script src="http://static.toutiao.com/test.js" crossorigin></script>
複製代碼
這樣的話,異域的test.js文件中發生異常時即可以被當前域的onerror監聽捕獲到詳細的異常信息。