前端分析性能監控(2019-9)

業界案例

目前前端性能監控系統大致爲分兩類:以GA爲代表的代碼監控和以webpagetest爲代表的工具監控。
代碼監控依託於js代碼並部署到需監控的頁面,手動計算時間差或者使用瀏覽器的的API進行數據統計。

影響代碼監控數據的因素有以下幾種:

  1. 瀏覽器渲染機制
  2. 瀏覽器對API的實現程度,比如 performance API
  3. 工具監控不用統計代碼部署到頁面中,一般依託於虛擬機。以 **webpage Test ** 爲例,輸入需要統計的URL 並且選擇運行URL的瀏覽器版本,webpage Test 後臺虛擬機對URL進行請求分析後給你各種性能指標,比如瀑布流、靜態文件數量、首屏渲染時間等

代碼監控和工具監控的對比如下表:

在這裏插入圖片描述
根據目前業務需求以及成本預算,最終決定採用代碼監控方案。以下分別介紹代碼監控各方面的實現細節。

前端性能監控指標

前端性能統計的數據大致有以下幾個:

  • 白屏時間:從打開網站到有內容渲染出來的時間節點;
  • 首屏時間:首屏內容渲染完畢的時間節點;
  • 用戶可操作時間節點:domready觸發節點;
  • 總下載時間:window.onload的觸發節點。
  • 下面介紹幾種以上幾個數據的統計方案。

1.常規統計方案

使用注入代碼監控的方式統計以上指標,在沒有一些瀏覽器新API(如下文將提到的timing API)的支持下,得到的數據大都是估值,雖然不準確,但也有一定的參考價值。

1.1 白屏時間

白屏時間節點指的是從用戶進入網站(輸入url、刷新、跳轉等方式)的時刻開始計算,一直到頁面有內容展示出來的時間節點。這個過程包括dns查詢、建立tcp連接、發送首個http請求(如果使用https還要介入TLS的驗證時間)、返回html文檔、html文檔head解析完畢。

使用注入代碼監控無法獲取解析html文檔之前的時間信息,目前普遍使用的白屏時間統計方案是在html文檔的head中所有的靜態資源以及內嵌腳本/樣式之前記錄一個時間點,在head最底部記錄另一個時間點,兩者的差值作爲白屏時間。

1.2 首屏時間

首屏時間的統計比較複雜,目前應用比較廣的方案是將首屏的圖片、iframe等資源添加onload事件,獲取最慢的一個。

這種方案比較適合首屏元素數量固定的頁面,比如移動端首屏不論屏幕大小都展示相同數量的內容,響應式得改變內容的字體、尺寸等。但是對於首屏元素不固定的頁面,這種方案並不適用,最典型的就是PC端頁面,不同屏幕尺寸下展示的首屏內容不同。上述方案便不適用於此場景。

1.3 可操作時間

用戶可操作的時間節點即dom ready觸發的時間,使用jquery可以通過$(document).ready()獲取此數據,如果不使用jQuery可以參考這裏通過原生方法實現dom ready。

1.4 總下載時間

總下載時間即window.onload觸發的時間節點。

目前大多數web產品都有異步加載的內容,比如圖片的lazyload等。如果總下載時間需要統計到這些數據,可以借鑑AOP的理念,在請求異步內容之前和之後分別打點,最後計算差值。不過通常來講,我們說的總下載時間並不包括異步加載的內容。

使用window.performance API

window.performance 是W3C性能小組引入的新的API,目前IE9以上的瀏覽器都支持。一個performance對象的完整結構如下圖所示:

在這裏插入圖片描述
在這裏插入圖片描述memory字段代表JavaScript對內存的佔用。

navigation字段統計的是一些網頁導航相關的數據:

redirectCount:重定向的數量(只讀),但是這個接口有同源策略限制,即僅能檢測同源的重定向;
type 返回值應該是0,1,2 中的一個。分別對應三個枚舉值:
0 : TYPE_NAVIGATE (用戶通過常規導航方式訪問頁面,比如點一個鏈接,或者一般的get方式)
1 : TYPE_RELOAD (用戶通過刷新,包括JS調用刷新接口等方式訪問頁面)
2 : TYPE_BACK_FORWARD (用戶通過後退按鈕訪問本頁面)
最重要的是timing字段的統計數據,它包含了網絡、解析等一系列的時間數據

timing API

timing的整體結構如下圖所示:

在這裏插入圖片描述
對我們比較有用的頁面性能數據大概包括如下幾個:

DNS查詢耗時、TCP鏈接耗時、request請求耗時、解析dom樹耗時、白屏時間、domready時間、onload時間等,而這些參數是通過上面的performance.timing各個屬性的差值組成的,計算方法如下:

DNS查詢耗時 :domainLookupEnd - domainLookupStart

TCP鏈接耗時 :connectEnd - connectStart

request請求耗時 :responseEnd - responseStart

解析dom樹耗時 : domComplete- domInteractive

白屏時間 :responseStart - navigationStart

domready時間 :domContentLoadedEventEnd - navigationStart

onload時間 :loadEventEnd - navigationStart

NavigationTiming的目的是用於分析頁面整體性能指標。如果要獲取個別資源(例如JS、圖片)的性能指標,就需要使用Resource Timing API。 示例如下:

TestResource(resourcesObj) => {
		let resourceArr = [];
		let len = resourcesObj.length;
		for(var i = len - 1;i >0;i--){
			let temp = {};
			let cur = resourcesObj[i];
			temp.key = cur.name;
			temp.resValue = cur.responseEnd - cur.requestStart + "ms";
			temp.conValue = cur.connectEnd - cur.connectStart + "ms";
			resourceArr.push(temp);
		}
		return resourceArr;
	}

JavaScript代碼異常監控

JavaScript異常一般有兩方面:語法錯誤和運行時錯誤。兩種錯誤的捕獲和處理方式不同,從而影響具體的方案選型。通常來說,處理JS異常的方案有兩種:try…catch捕獲 和 window.onerror捕獲。以下就兩種方案分別分析各自的優劣。

雖然語法錯誤本應該在開發構建階段使用測試工具避免,但難免會有馬失前蹄部署到線上的時候。

try…catch捕獲

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

try{
//可能發生異常的代碼段
}catch(e){
//將異常信息發送服務端
}

try…catch的優點是可以細化到每個代碼塊,並且可以自定義錯誤信息以便統計。
具體到上文提到的兩種js異常,try…catch無法捕獲語法錯誤,當遇到語法錯誤時,瀏覽器仍然會拋出錯誤Uncaught SyntaxError,但是不會被捕獲,不會走進catch的代碼塊內。

window.onerror捕獲

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

window.onerror事件的詳細信息參考這裏。

/**
 * @param {String}  errorMessage   錯誤信息
 * @param {String}  scriptURL      出錯文件的URL
 * @param {Long}    lineNumber     出錯代碼的行號
 * @param {Long}    columnNumber   出錯代碼的列號
 * @param {Object}  errorObj       錯誤信息Object
 */
window.onerror = function(errorMessage, scriptURL, lineNumber,columnNumber,errorObj) { 
    // code..
}

onerror的實現方式各瀏覽器略有差異,但是前三個參數都是相同的,某些低版本瀏覽器沒有後兩個參數。

最後一個參數errorObj各瀏覽器實現的程度不一致,具體可參考這裏。

下圖是被onerror捕獲到的一個異常的具體信息:

綜上所述,window.onerror方案的優點是減少了開發人員的工作量,部署方便,並且可以捕獲語法錯誤和運行錯誤。缺點是錯誤信息不能自定義,並且errorObj每種瀏覽器的實現有略微差異,導致需統計的信息有侷限性。

跨域JS文件異常的捕獲

爲了提高web性能,目前大部分web產品架構中都有CDN這一環,將資源部署到不同的域名上,充分利用瀏覽器的併發請求機制。那麼在跨域JS文件中發生異常的時候,onerror監聽會捕獲到什麼信息呢?請看下圖:

只有一個稍微有價值的信息Script error,其他什麼信息都沒有,爲什麼會這樣呢?

我們都知道瀏覽器有同源資源限制,常規狀態下是無法進行跨域請求的。而script、img、iframe標籤的src屬性是沒有這種限制的,這也是很多跨域方案的基礎。但是即使script標籤可以請求到異域的js文件,此文件中的信息也並不能暴露到當前域內,這也是瀏覽器的安全措施所致。

那麼有沒有辦法獲取到異域資源的異常信息呢?

其實很簡單,目前可以說基本上所有的web產品對於js/css/image等靜態資源都在服務端設置了Access-Control-Allow-Origin: *的響應頭,也就是允許跨域請求。在這個環境下,只要我們在請求跨域資源的script標籤上添加一個crossorigin屬性即可:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章