在當下互聯網行業,監控概念與重要性已經不須要再進行闡述,然而監控分爲多種,對物理層(機房,雲主機)的監控,對傳輸鏈路的監控,對已部署服務的監控等等,然後端的代碼一般直接運行在服務器並處於24小時實時的監控狀態之下,一旦服務的可用性出現問題,SRE和DEV每每在第一時間就會收到告警,並根據告警信息在第一時間解決故障。相比之下,前端代碼則運行在客戶端上,爲了讓前端可以和後端同樣,須要將客戶端的前端代碼監控起來,當客戶端出現故障時,能第一時間通知到前端負責人,定位故障,及時止損。javascript
那前端監控系統都須要監控什麼?在前端應用日漸複雜的今天,我認爲對於前端的監控主要分爲三個方面:html
性能監控前端
爲何要監控性能呢?由於對於任何一家互聯網公司,性能每每與利益直接相關。有數據調查顯示:當Google 延遲 400ms時,搜索量降低 0.59%、Bing 延遲 2s,收入降低 4.3%、Yahoo 延遲 400ms,流量降低 5-9%,因此,不少公司在作用戶體驗分析時,第一個看的就是性能監控指標,在前端領域,性能無非是如下參考指標:java
並且很重要的一點,也是你們每每最容易忽視的:性能會伴隨產品的迭代而有所衰減。特別在移動端,網絡條件十分不穩定的狀況下。性能優化不存在「黃金法則」,咱們須要一套性能監控系統持續監控、評估、預警頁面性能情況、發現瓶頸,更有針對性的指導優化工做的進行。
webpack
異常監控
web
除了性能以外,咱們還要監控客戶端腳本發生的報錯,前端報錯受網絡,機型,業務邏輯影響並且大部分錯誤難以還原現場,好比咱們團隊時時收到用戶的反饋和投訴:ajax
面對用戶的反饋,開發常常感到困惑:到底有多卡,哪一個步驟卡?是個別現象仍是大面積都受到了影響?白屏時頁面請求的返回碼是多少?是被運行商劫持仍是CDN出了問題?能讓用戶用Charles配合抓個包麼?如何作有針對性的優化?優化的結果怎麼去衡量?算法
爲了解決這些痛點,咱們須要對客戶端服務進行基於用戶行爲的監控。chrome
數據監控數據庫
互聯網公司的產品,每個決策,每個迭代都須要分析各類數據,數據中每每會有咱們須要的答案:
這部分監控數據主要供PM/PD使用,業務數據能夠驅動業務自身的增加,有人曾說:「要下降創業失敗的可能性只有兩種方法:一是未卜先知,另外一個是作精益的數據分析」,因而可知數據分析的重要性。
目前已經存在了一些針對前端監控解決方案:Sentry,Badjs,jsTracker,GrowingIo等等,在公司內部也有自研的監控系統。它們都從不一樣維度試着解決前端在監控方面的問題,你們的實現思路都很相似、要實現監控,首先要採集指標:
這裏要針對的主要是白屏時間、首屏時間、用戶可操做、總下載時間。
這裏以首屏時間爲例:高版本chrome瀏覽器中能夠直接經過 firstPaintTime 接口來獲取load time,但大部分瀏覽器並不支持,必須想其餘辦法來監測。謹記一點,在作時間相關測量時,千萬不要使用setTimeout和setInterval方法,由於在單線程執行引擎中,異步隊列的執行是不能確保執行時間的。這邊給出一種可行的測量方案,準確率在99成以上。
<doctype html>
<html>
<head>
<script type="text/javascript">
var timerStart = Date.now();
</script>
<!-- 加載其餘資源,執行代碼blabla -->
</head>
<body>
<!-- 路由框架掛載節點 -->
<script type="text/javascript">
$(document).ready(function() {
console.log("DOMready 時間 ", Date.now()-timerStart);
});
$(window).load(function() {
console.log("全部資源加載完成 時間: ", Date.now()-timerStart);
});
</script>
</body>
</html>
複製代碼
另外一種優雅的解決方案是直接使用window.performance接口:
connectEnd Time when server connection is finished.
connectStart Time just before server connection begins.
domComplete Time just before document readiness completes.
domContentLoadedEventEnd Time after DOMContentLoaded event completes.
domContentLoadedEventStart Time just before DOMContentLoaded starts.
domInteractive Time just before readiness set to interactive.
domLoading Time just before readiness set to loading.
domainLookupEnd Time after domain name lookup.
domainLookupStart Time just before domain name lookup.
fetchStart Time when the resource starts being fetched.
loadEventEnd Time when the load event is complete.
loadEventStart Time just before the load event is fired.
navigationStart Time after the previous document begins unload.
redirectCount Number of redirects since the last non-redirect.
redirectEnd Time after last redirect response ends.
redirectStart Time of fetch that initiated a redirect.
requestStart Time just before a server request.
responseEnd Time after the end of a response or connection.
responseStart Time just before the start of a response.
timing Reference to a performance timing object.
navigation Reference to performance navigation object.
performance Reference to performance object for a window.
type Type of the last non-redirect navigation event.
unloadEventEnd Time after the previous document is unloaded.
unloadEventStart Time just before the unload event is fired.複製代碼
接口兼容性:
異常指標
主動捕獲異常方案主要是 onError 和 addEventListener,onError 在 IE6 開始就支持了,因此 大部分系統的主動採集是使用的 onError。這裏注意瀏覽器的同源性策略(CORS),在高級瀏覽器中若是瀏覽器捕獲到了錯誤信息,若是 JS 文件所在的域名(如:meituan.com)和當前的頁面地址(如:dianping.com)是跨域的,那麼引擎會自動把onError 中的參數 替換爲 script error,此時沒法獲取行列數以及報錯詳細信息。解決方案是在標籤引入時加上crossorigin字段。
雖然傳統方法可以自動catch大部分錯誤,可是也伴隨着如下缺陷:
數據指標
傳統監控方案採用的都是手動埋點上報,可是缺點十分明顯:手動埋點每每會出現埋點混亂,甚至埋錯、漏埋的問題,埋點溝經過程中,數據團隊和業務工程團隊配合困難,新功能的開發每每伴隨着新埋點的增長,並且數據團隊的需求優先級每每靠後,致使不少新上線功能得不到數據的驗證。
而有些基於關係型數據庫的系統實時性差,數據要隔天才能查看,查詢命令執行一次動輒耗費幾十分鐘乃至上小時,已經沒有效率可言。
伴隨着上面討論的問題,咱們尋求新的解決方案,一種高可用的監控方案。它應該具備以下特徵:
根據這些需求,咱們團隊打造了一套全新的監控體系,新系統採用了無埋點SDK(小程序),ELK作本地化日誌存儲,並使用了基於動態閾值的告警策略。下面是系統架構圖:
開發人員在本地經過代碼接入SDK後,便可使用監控體系的所有功能:數據採集,上報,聚合分析,智能告警等功能,並且全部數據均是實時上報,秒級查詢。
在最開始探索過程當中,咱們使用webpack插件+npm包下載方式,可是因爲兩部分上報邏輯在網絡極差的狀況下,會出現寫緩存衝突的問題,致使重複上報或錯誤上報,現已將架構調整爲單一script標籤引入的方式,部分保留下來的主動上報接口,開發能夠根據本身須要在業務代碼中再次封裝:
...
moduleClick(options) {
const { name, ...otherOptions } = options;
M.moduleClick(name, otherOptions);
},
/**
* 曝光事件
* @param options
*/
moduleView(options) {
const { name, ...otherOptions } = options;
M.moduleView(name, otherOptions);
},
/**
* 編輯事件
* @param options
*/
moduleEdit(options) {
const { name, ...otherOptions } = options;
M.moduleEdit(name, otherOptions);
},
...
複製代碼
接入後,新版系統和以前相比有哪些變化?
1.由於採集是無埋點全量的,關鍵方法都會進行參數上報,而後能夠經過分類聚合創建用戶的操做時序,經過故障上下文準肯定位問題。
2. 對resource和ajax請求指標作採集,能夠篩選出故障用戶當時的場景信息:
3.告警採用動態閾值,對於週期性強的數據,經過機器學習的算法進行環比告警,大大下降了誤報和漏報:
在作日誌存儲的時候,數據量是一個挑戰,咱們採用的是集羣架構,可是一個用戶量很大的站點,日誌的上報量是很是高的,高峯期時,一個1萬日活APP可能會突破3000的qps,這對日誌系統併發能力和穩定性是很大的挑戰。咱們選擇了全量master+data節點的方式,對數據副本分片設置爲1,任意一臺節點掛掉,會由副本選舉出新的主分片,不會形成日誌丟失。在寫入方面,咱們選擇了bulk方式批量寫入,經過反覆試驗,批量寫入線程大小在5MB-15MB之間。因爲日誌系統是主要面對寫入的,因此關閉了_all查詢,寫入性能優化1倍,同時gc新老分配爲1:4,保證了批量寫入的穩定。
經過對新監控解決方案的探索,咱們積累了比較寶貴的數據分析經驗,對於終端哪些數據對於故障處理,性能優化起到重要做用有了新的認識,不過目前系統仍處於迭代過程當中,距離預約的目標還有比較大的優化空間。
在將來,咱們將重點攻克如下幾個問題:
減小上報量:合併數據結構,釋放更多上行帶
優化SDK性能:減小緩存寫入頻率,作到業務對監控模塊無感
識別周高峯和節假日,同時加強數據清洗能力,提升數據的可用性
3.優化數據分析體驗
開放埋點配置平臺,讓產品自主配置業務埋點,經過配置文件轉化成埋點,省時高效。