掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
近年來,前端監(jiān)控是越來越火,目前已經(jīng)有很多成熟的產(chǎn)品供我們選擇使用,如下圖所示

有這么多監(jiān)控平臺,那為什么還要學(xué)習(xí)自研前端監(jiān)控?
前端監(jiān)控的第一個步驟就是數(shù)據(jù)采集,采集的信息包含環(huán)境信息、性能信息、異常信息、業(yè)務(wù)信息。
環(huán)境信息是每個監(jiān)控系統(tǒng)必備的內(nèi)容,畢竟排查問題的時候需要知道來自哪個頁面、瀏覽器是誰、操作用戶是誰……,這樣才能快速定位問題,解決問題。一般這些常見的環(huán)境信息主要包含:
頁面的性能直接影響了用戶留存率,,Google DoubleClick 研究表明:如果一個移動端頁面加載時長超過 3 秒,用戶就會放棄而離開。BBC 發(fā)現(xiàn)網(wǎng)頁加載時長每增加 1 秒,用戶就會流失 10%。,Google DoubleClick 研究表明:如果一個移動端頁面加載時長超過 3 秒,用戶就會放棄而離開。BBC 發(fā)現(xiàn)網(wǎng)頁加載時長每增加 1 秒,用戶就會流失 10%。所以我們的追求就是提高頁面的性能,為了提高性能需要監(jiān)控哪些指標(biāo)呢?
指標(biāo)有很多,我總結(jié)為以下兩個方面:網(wǎng)絡(luò)層面和頁面展示層面。
從網(wǎng)絡(luò)層面來看涉及的指標(biāo)有:重定向耗時、DNS解析耗時、TCP連接耗時、SSL耗時、TTFB網(wǎng)絡(luò)請求耗時、數(shù)據(jù)傳輸耗時、資源加載耗時……,各個指標(biāo)的解釋如下表所示:
頁面展示層面的指標(biāo)是針對用戶體驗提出的幾個指標(biāo),包含F(xiàn)P、FCP、LCP、FMP、DCL、L等,這幾個指標(biāo)其實就是chrome瀏覽器中performance模塊的指標(biāo)(如圖所示)。
各個指標(biāo)的解釋如下表所示。
上述這么多指標(biāo)該怎么獲取呢?瀏覽器給我們留了相應(yīng)的接口——神奇的window.performance,通過該接口可以獲取一些列與性能相關(guān)的參數(shù),下面以https://baidu.com 為例來看一下與這些指標(biāo)相關(guān)的參數(shù):
window.performance中的timing屬性中的內(nèi)容不就是為了求解上述指標(biāo)所需要的值嗎?看著上面的屬性值再對應(yīng)下面的performance訪問流程圖,整個過程是不是一目了然。
有了上面的值我們就一起求解上述的指標(biāo):
Google工程師一直在推動以用戶為中心的性能指標(biāo),所以頁面展示層面的變化較大,求解方式稍有不同:
通過window.performance.getEntriesByType(‘paint’)的方式獲取。
const paint = window.performance.getEntriesByType('paint');
const FP = paint[0].startTime,
const FCP = paint[1].startTime,function getLCP() {
// 增加一個性能條目的觀察者
new PerformanceObserver((entryList, observer) => {
let entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1];
observer.disconnect();
console.log('LCP', lastEntry.renderTime || lastEntry.loadTime);
}).observe({entryTypes: ['largest-contentful-paint']});
}function getFMP() {
let FMP;
new PerformanceObserver((entryList, observer) => {
let entries = entryList.getEntries();
observer.disconnect();
console.log('FMP', entries);
}).observe({entryTypes: ['element']});
}domContentLoadEventEnd – fetchStart
loadEventStart – fetchStart
domInteractive – fetchStart
function getFID() {
new PerformanceObserver((entryList, observer) => {
let firstInput = entryList.getEntries()[0];
if (firstInput) {
const FID = firstInput.processingStart - firstInput.startTime;
console.log('FID', FID);
}
observer.disconnect();
}).observe({type: 'first-input', buffered: true});
}對于網(wǎng)站來說,異常信息是最致命、最影響用戶體驗的問題,需要重點(diǎn)監(jiān)控。對于異常信息可以分為兩類:運(yùn)行時錯誤、接口錯誤。下面就分別來嘮一嘮這兩類錯誤。
當(dāng)JavaScript運(yùn)行時有可能會發(fā)生錯誤,可歸類為七種:語法錯誤、類型錯誤、范圍錯誤、引用錯誤、eval錯誤、URL錯誤、資源加載錯誤。為了捕獲代碼錯誤,需要考慮兩類場景:非Promise場景和Promise場景,因為兩種場景捕獲錯誤的策略不同。
非Promise場景可通過監(jiān)聽error事件來捕獲錯誤。對于error事件捕獲的錯誤分為兩類:資源錯誤和代碼錯誤。資源錯誤指的就是js、css、img等未加載,該錯誤只能在捕獲階段獲取到,且為資源錯誤時event.target.localName存在值(用此區(qū)分資源錯誤與代碼錯誤);代碼錯誤指的就是語法錯誤、類型錯誤等這一類錯誤,可以獲取代碼錯誤的信息、堆棧等,用于排查錯誤。
export function listenerError() {
window.addEventListener('error', (event) => {
if (event.target.localName) {
console.log('這是資源錯誤', event);
}
else {
console.log('這是代碼錯誤', event);
}
}, true)
}Promise場景的處理方式有所不同,當(dāng)Promise被reject且沒有reject處理器的時候,會觸發(fā)unhandlerejection事件,所以通過監(jiān)聽unhandlerejection的事件來捕獲錯誤。
export function listenerPromiseError() {
window.addEventListener('unhandledrejection', (event) => {
console.log('這是Promise場景中錯誤', event);
})
}對于瀏覽器來說,所有的接口均是基于XHR和Fetch實現(xiàn)的,為了捕獲接口中的錯誤,可以通過重寫該方法,然后通過接口返回的信息來判斷當(dāng)前接口的狀況,下面以XHR為例來展示封裝過程。
function newXHR() {
const XMLHttpRequest = window.XMLHttpRequest;
const oldXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = (method, url, async) => {
// 做一些自己的數(shù)據(jù)上報操作
return oldXHROpen.apply(this, arguments);
}
const oldXHRSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = (body) => {
// 做一些自己的數(shù)據(jù)上報操作
return oldXHRSend.apply(this, arguments);
}
}每個產(chǎn)品都會有自己的業(yè)務(wù)信息,例如用戶在線時長、pv、uv、用戶分布等,通過獲取這些業(yè)務(wù)信息才能更加清楚的了解目前產(chǎn)品的狀況,以便產(chǎn)品經(jīng)理更好的去規(guī)劃產(chǎn)品的未來方向。由于每個產(chǎn)品業(yè)務(wù)信息多種多樣,小伙伴本可以按照自己的需求進(jìn)行撰寫代碼,此處我就不再贅述。
對于上報的方式無外乎兩種:一種是Ajax的方式上報;另一種是通過Image的形式進(jìn)行上報。目前很多大廠采用的上報方式均是通過一個1*1像素的的gif圖片進(jìn)行上報,既然人家都采用該種策略,那我們就來嘮一嘮下面兩個問題。
為什么采用Image的方式上報?
圖片類型很多,為什么采用gif這種格式進(jìn)行上報?
其實歸結(jié)為一個字——小。對于1*1px的圖片,BMP結(jié)構(gòu)的文件需要74字節(jié),PNG結(jié)構(gòu)的文件需要67字節(jié),GIF結(jié)構(gòu)的文件只需要43字節(jié)。同樣的響應(yīng),GIF可以比BMP節(jié)約41%的流量,比PNG節(jié)約35%的流量,所以選擇gif進(jìn)行上報。
日志上報之后需要進(jìn)行清洗,獲取自己所需要內(nèi)容,并將分析內(nèi)容進(jìn)行存儲。根據(jù)數(shù)據(jù)量的大小可分為兩種方式:單機(jī)和集群。
訪問量小、日志少的網(wǎng)站可以采用單機(jī)的方式對數(shù)據(jù)進(jìn)行分析,例如用node讀取日志文件,然后通過日志文件中獲取所需要的信息,最終將處理的信息存儲到數(shù)據(jù)庫中。
很多產(chǎn)品的訪問量很大,日志很多,此時就需要利用Hadoop進(jìn)行分布式處理,獲取最終處理結(jié)果,其處理流程圖如下所示:
根據(jù)自己的日志量級決定自己的分析方式,合適的就是最好的,不用一味追求最優(yōu)的、最處理方式。
當(dāng)異常類型超多一定閾值之后需要進(jìn)行報警通知,讓對應(yīng)的工作人員去處理問題,及時止損。根據(jù)報警的級別不同,可以選擇不同的報警方式。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流