深入淺出前端監(jiān)控
點(diǎn)擊上方?前端陽(yáng)光,關(guān)注公眾號(hào)
回復(fù)加群,加入技術(shù)交流群交流群

背景
近期主要工作內(nèi)容是進(jìn)校開(kāi)放平臺(tái)(簡(jiǎn)稱開(kāi)平)相關(guān)業(yè)務(wù),開(kāi)平簡(jiǎn)單來(lái)說(shuō)就是一個(gè)可為第三方應(yīng)用提供接入主端(例如微信、飛書(shū))應(yīng)用能力的平臺(tái),為了讓第三方應(yīng)用穩(wěn)定可靠地接入開(kāi)平,需要為其提供一些底層的基礎(chǔ)能力,其中應(yīng)用監(jiān)控就是其中不可或缺的一環(huán)。目前如何在進(jìn)校開(kāi)平中做三方應(yīng)用的監(jiān)控管理還在初步預(yù)研階段,為此了解了一下前端監(jiān)控相關(guān)背景知識(shí)。鑒于我司已有一套非常完善的 APM 平臺(tái),因此下文諸多理論和源碼參考自我司 APM Web SDK 源碼。
監(jiān)控流程

數(shù)據(jù)采集:明確需要采集哪些指標(biāo)以及采集的方式。
數(shù)據(jù)上報(bào):將上一步采集的數(shù)據(jù)以一定的策略進(jìn)行上報(bào)。
數(shù)據(jù)清洗、存儲(chǔ):服務(wù)端在接收到上報(bào)數(shù)據(jù)后需要對(duì)數(shù)據(jù)進(jìn)行清洗和存儲(chǔ)。
數(shù)據(jù)消費(fèi):數(shù)據(jù)最終會(huì)在類似 Slardar Web 這樣的監(jiān)控平臺(tái)以圖、表等形式分類別地進(jìn)行可視化展示,并提供諸如監(jiān)控報(bào)警等消費(fèi)能力。
上述流程看似不復(fù)雜但每個(gè)環(huán)節(jié)的技術(shù)細(xì)節(jié)都非常多,本文主要關(guān)注前端視角下的數(shù)據(jù)采集和上報(bào)環(huán)節(jié)。
數(shù)據(jù)采集
做好前端監(jiān)控的第一步要明確哪些數(shù)據(jù)是值得我們采集的,前端環(huán)境下監(jiān)控?cái)?shù)據(jù)從大的維度上可劃分成環(huán)境信息、異常數(shù)據(jù)和性能數(shù)據(jù):

環(huán)境信息
采集的監(jiān)控?cái)?shù)據(jù)一般都會(huì)設(shè)置一些通用的環(huán)境信息,這些環(huán)境信息可以提供更多的維度以幫助用戶發(fā)現(xiàn)問(wèn)題和解決問(wèn)題。下圖列舉了一些常見(jiàn)的環(huán)境信息:

異常監(jiān)控
JS 異常
Script Error
先拋開(kāi)如何采集 JS 異常信息不談,在采集之前如果連報(bào)錯(cuò)信息都不全那么即使采集到了這樣的數(shù)據(jù)也是無(wú)效的。巧的是,確實(shí)存在這樣一個(gè)場(chǎng)景:當(dāng)頁(yè)面加載自不同域的腳本(例如頁(yè)面的 JS 托管在 CDN)中發(fā)生語(yǔ)法錯(cuò)誤時(shí),瀏覽器基于安全機(jī)制考慮,不會(huì)給出語(yǔ)法錯(cuò)誤的細(xì)節(jié),而是簡(jiǎn)單的?Script error.?。
因此,如果你希望自己頁(yè)面的詳細(xì)報(bào)錯(cuò)信息被監(jiān)控 SDK 捕獲你需要為頁(yè)面中的腳本 script 添加?crossorigin= anonymous?屬性,且腳本所在的服務(wù)設(shè)置 CORS 響應(yīng)頭?Access-Control-Allow-Origin: *?,這是 JS 異常監(jiān)控的第一項(xiàng)準(zhǔn)備工作。
編譯時(shí)與運(yùn)行時(shí)錯(cuò)誤
常見(jiàn)的 JS 錯(cuò)誤可分為編譯時(shí)錯(cuò)誤和運(yùn)行時(shí)錯(cuò)誤,其中編譯時(shí)錯(cuò)誤在 IDE 層面就會(huì)給出提示,一般不會(huì)流入到線上,因此編譯時(shí)錯(cuò)誤不在監(jiān)控范圍。

有的同學(xué)說(shuō)在 Slardar 上時(shí)常看到?SyntaxError?的字樣,這種情況一般都是?JSON.parse?解析出錯(cuò)或?yàn)g覽器兼容性問(wèn)題導(dǎo)致,屬于運(yùn)行時(shí)錯(cuò)誤并非編譯時(shí)錯(cuò)誤。

對(duì)于異常監(jiān)控我們主要關(guān)注 JS?運(yùn)行時(shí)錯(cuò)誤,多數(shù)場(chǎng)景下的處理手段如下:
| 錯(cuò)誤場(chǎng)景 | 如何上報(bào) |
|---|---|
| 場(chǎng)景一:自行感知的同步運(yùn)行時(shí)異常 | try-catch?后進(jìn)行錯(cuò)誤上報(bào) |
| 場(chǎng)景二:沒(méi)有手動(dòng) catch 的運(yùn)行時(shí)異常(包括異步但不包括 promise 異常) | 通過(guò)?window.onerror進(jìn)行監(jiān)聽(tīng) |
| 場(chǎng)景三:自行感知的 promise 異常 | promise catch?進(jìn)行捕獲后進(jìn)行錯(cuò)誤上報(bào) |
| 場(chǎng)景四:沒(méi)有手動(dòng) catch 的 promise 異常 | 監(jiān)聽(tīng) window 對(duì)象的?unhandledrejection?事件 |
整體來(lái)看,監(jiān)控 SDK 會(huì)在全局幫助用戶去捕獲他們沒(méi)有自行感知的異常并上報(bào),對(duì)于自行捕獲的異常一般會(huì)提供手動(dòng)上報(bào)接口進(jìn)行上報(bào)。
SourceMap
假設(shè)現(xiàn)在已經(jīng)采集到頁(yè)面目前存在的 JS 異常并做了上報(bào),最終消費(fèi)時(shí)你我們當(dāng)然希望看到的是錯(cuò)誤的初始來(lái)源和調(diào)用堆棧,但實(shí)際發(fā)生報(bào)錯(cuò)的 JS 代碼都經(jīng)過(guò)各種轉(zhuǎn)換混淆壓縮,早已面目全非了,因此這里需要借助打包階段生成的 SourceMap 做一個(gè)反向解析得到原始報(bào)錯(cuò)信息的上下文。
以 Sentry (Slardar 也有用到 Sentry)為例大致流程如下:
采集側(cè)收集錯(cuò)誤信息發(fā)送到監(jiān)控平臺(tái)服務(wù)端。
接入的業(yè)務(wù)方自行上傳 SourceMap 文件到監(jiān)控平臺(tái)服務(wù)端,上傳完成后刪除本地的 SourceMap文件,且打包后的 js 文件末尾不需要 SourceMap URL,最大程度避免 SourceMap 泄漏。
服務(wù)端通過(guò) source-map 工具結(jié)合 SourceMap 和原始錯(cuò)誤信息定位到源碼具體位置。
靜態(tài)資源加載異常
靜態(tài)資源加載異常的捕獲存在兩種方式:
在出現(xiàn)靜態(tài)資源加載異常的元素的? onerror?方法中處理。
資源加載異常觸發(fā)的 error事件不會(huì)冒泡,因此使window.addEventListener('error', cb,?true)?在事件捕獲階段進(jìn)行捕獲。
第一種方式侵入性太強(qiáng),不夠優(yōu)雅,目前主流方案均采用第二種方式進(jìn)行監(jiān)控:

捕獲靜態(tài)資源加載異常
APM 平臺(tái)一般會(huì)有所有靜態(tài)資源加載的明細(xì),其原理是通過(guò) PerformanceResourceTiming API 來(lái)采集靜態(tài)資源加載的基本情況,這里不做展開(kāi)。
請(qǐng)求異常
業(yè)務(wù)中的 AJAX 請(qǐng)求或者 Fetch 請(qǐng)求在不同的網(wǎng)絡(luò)環(huán)境或者客戶端環(huán)境會(huì)有不穩(wěn)定的表現(xiàn),這些不穩(wěn)定的情況我們很難通過(guò)本地測(cè)試的途徑進(jìn)行測(cè)試或者感知得到,所以我們需要對(duì) HTTP 請(qǐng)求進(jìn)行線上監(jiān)控,通過(guò)將 HTTP 請(qǐng)求異常上報(bào)的方式對(duì)錯(cuò)誤日志進(jìn)行采集,然后進(jìn)行一系列的分析和監(jiān)控。
請(qǐng)求異常通常泛指 HTTP 請(qǐng)求失敗或者 HTTP 請(qǐng)求返回的狀態(tài)碼非 20X。
那么請(qǐng)求異常監(jiān)控怎么做呢?普遍采用的方式是對(duì)原生的?XMLHttpRequest?對(duì)象和?fetch?方法進(jìn)行重寫(xiě),從而在代理對(duì)象中實(shí)現(xiàn)狀態(tài)碼的監(jiān)聽(tīng)和錯(cuò)誤上報(bào):

重寫(xiě) XMLHttpRequest 對(duì)象

重寫(xiě) fetch 方法
當(dāng)然了,重寫(xiě)上述方法后除了異常請(qǐng)求可以被監(jiān)控到之外,正常響應(yīng)的請(qǐng)求狀態(tài)自然也能被采集到,比如 Slardar 會(huì)將對(duì)所有上報(bào)請(qǐng)求的持續(xù)時(shí)間進(jìn)行分析從而得出慢請(qǐng)求的占比:

PS:如果通過(guò) XHR 或 fetch 來(lái)上報(bào)監(jiān)控?cái)?shù)據(jù)的話,上報(bào)請(qǐng)求也會(huì)被被攔截,可以有選擇地做一層過(guò)濾處理。
卡頓異常
卡頓指的是顯示器刷新時(shí)下一幀的畫(huà)面還沒(méi)有準(zhǔn)備好,導(dǎo)致連續(xù)多次展示同樣的畫(huà)面,從而讓用戶感覺(jué)到頁(yè)面不流暢,也就是所謂的掉幀,衡量一個(gè)頁(yè)面是否卡頓的指標(biāo)就是我們熟知的 FPS。
如何獲取 FPS
Chrome DevTool 中有一欄 Rendering 中包含 FPS 指標(biāo),但目前瀏覽器標(biāo)準(zhǔn)中暫時(shí)沒(méi)有提供相應(yīng) API ,只能手動(dòng)實(shí)現(xiàn)。這里需要借助?requestAnimationFrame?方法模擬實(shí)現(xiàn),瀏覽器會(huì)在下一次重繪之前執(zhí)行?rAF?的回調(diào),因此可以通過(guò)計(jì)算每秒內(nèi)?rAF?的執(zhí)行次數(shù)來(lái)計(jì)算當(dāng)前頁(yè)面的 FPS。

通過(guò) rAF 計(jì)算 FPS
如何上報(bào)“真實(shí)卡頓”
從技術(shù)角度看 FPS 低于 60 即視為卡頓,但在真實(shí)環(huán)境中用戶很多行為都可能造成 FPS 的波動(dòng),并不能無(wú)腦地把 FPS 低于 60 以下的 case 全部上報(bào),會(huì)造成非常多無(wú)效數(shù)據(jù),因此需要結(jié)合實(shí)際的用戶體驗(yàn)重新定義“真正的卡頓”,這里貼一下司內(nèi) APM 平臺(tái)的上報(bào)策略:
頁(yè)面?FPS?持續(xù)低于預(yù)期:當(dāng)前頁(yè)面連續(xù) 3s FPS 低于 20。
用戶操作帶來(lái)的卡頓:當(dāng)用戶進(jìn)行交互行為后,渲染新的一幀的時(shí)間超過(guò) 16ms + 100ms。
崩潰異常

Web 頁(yè)面崩潰指在網(wǎng)頁(yè)運(yùn)行過(guò)程頁(yè)面完全無(wú)響應(yīng)的現(xiàn)象,通常有兩種情況會(huì)造成頁(yè)面崩潰:
JS 主線程出現(xiàn)無(wú)限循環(huán),觸發(fā)瀏覽器的保護(hù)策略,結(jié)束當(dāng)前頁(yè)面的進(jìn)程。
內(nèi)存不足
發(fā)生崩潰時(shí)主線程被阻塞,因此對(duì)崩潰的監(jiān)控只能在獨(dú)立于 JS 主線程的 Worker 線程中進(jìn)行,我們可以采用 Web Worker 心跳檢測(cè)的方式來(lái)對(duì)主線程進(jìn)行不斷的探測(cè),如果主線程崩潰,就不會(huì)有任何響應(yīng),那就可以在 Worker 線程中進(jìn)行崩潰異常的上報(bào)。這里繼續(xù)貼一下 Slardar 的檢測(cè)策略:
JS 主線程:
固定時(shí)間間隔(2s)向 Web Worker 發(fā)送心跳 Web Worker:
定期(2s)檢查是否收到心跳。 超過(guò)一定時(shí)間(6s)未收到心跳,則認(rèn)為頁(yè)面崩潰。 檢測(cè)到崩潰后,通過(guò) http 請(qǐng)求進(jìn)行異常上報(bào)。

崩潰檢測(cè)
性能監(jiān)控

性能監(jiān)控并不只是簡(jiǎn)單的監(jiān)控“頁(yè)面速度有多快”,需要從用戶體驗(yàn)的角度全面衡量性能指標(biāo)。(就是所謂的?RUM?指標(biāo))目前業(yè)界主流標(biāo)準(zhǔn)是 Google 最新定義的 Core Web Vitals:
加載(loading)?:LCP 交互(interactivity)?:FID 視覺(jué)穩(wěn)定(visual stability)?:CLS
可以看到最新標(biāo)準(zhǔn)中,以往熟知的 FP、FCP、FMP、TTI 等指標(biāo)都被移除了,個(gè)人認(rèn)為這些指標(biāo)還是具備一定的參考價(jià)值,因此下文還是會(huì)將這些指標(biāo)進(jìn)行相關(guān)介紹。(谷歌的話不聽(tīng)不聽(tīng)??)
Loading 加載
和 Loading 相關(guān)的指標(biāo)有?FP?、FCP?、FMP?和?LCP,首先來(lái)看一下我們相對(duì)熟悉的幾個(gè)指標(biāo):
FP/FCP/FMP

一張流傳已久的圖


FP?(First Paint):?當(dāng)前頁(yè)面首次渲染的時(shí)間點(diǎn),通常將開(kāi)始訪問(wèn) Web 頁(yè)面的時(shí)間點(diǎn)到 FP 的時(shí)間點(diǎn)的這段時(shí)間視為白屏?xí)r間,簡(jiǎn)單來(lái)說(shuō)就是有屏幕中像素點(diǎn)開(kāi)始渲染的時(shí)刻即為 FP。 FCP?(?First Contentful Paint?):?當(dāng)前頁(yè)面首次有內(nèi)容渲染的時(shí)間點(diǎn),這里的 內(nèi)容 通常指的是文本、圖片、svg 或 canvas 元素。
這兩個(gè)指標(biāo)都通過(guò) PerformancePaintTiming API 獲取:

通過(guò) PerformancePaintTiming 獲取 FP 和 FCP
下面再來(lái)看 FMP 的定義和獲取方式:
FMP?(First Meaningful Paint):?表示首次繪制有意義內(nèi)容的時(shí)間,在這個(gè)時(shí)刻,頁(yè)面整體布局和文字內(nèi)容全部渲染完成,用戶能夠看到頁(yè)面主要內(nèi)容,產(chǎn)品通常也會(huì)關(guān)注該指標(biāo)。
FMP 的計(jì)算相對(duì)復(fù)雜,因?yàn)闉g覽器并未提供相應(yīng)的 API,在此之前我們先看一組圖:


從圖中可以發(fā)現(xiàn)頁(yè)面渲染過(guò)程中的一些規(guī)律:
在 1.577 秒,頁(yè)面渲染了一個(gè)搜索框,此時(shí)已經(jīng)有 60 個(gè)布局對(duì)象被添加到了布局樹(shù)中。
在 1.760 秒,頁(yè)面頭部整體渲染完成,此時(shí)布局對(duì)象總數(shù)是 103 個(gè)。
在 1.907 秒,頁(yè)面主體內(nèi)容已經(jīng)繪制完成,此時(shí)有 261 個(gè)布局對(duì)象被添加到布局樹(shù)中從用戶體驗(yàn)的角度看,此時(shí)的時(shí)間點(diǎn)就是是 FMP。
可以看到布局對(duì)象的數(shù)量與頁(yè)面完成度高度相關(guān)。業(yè)界目前比較認(rèn)可的一個(gè)計(jì)算 FMP 的方式就是——「頁(yè)面在加載和渲染過(guò)程中最大布局變動(dòng)之后的那個(gè)繪制時(shí)間即為當(dāng)前頁(yè)面的 FMP 」
實(shí)現(xiàn)原理則需要通過(guò) MutationObserver 監(jiān)聽(tīng) document 整體的 DOM 變化,在回調(diào)計(jì)算出當(dāng)前 DOM 樹(shù)的分?jǐn)?shù),分?jǐn)?shù)變化最劇烈的時(shí)刻,即為?FMP?的時(shí)間點(diǎn)。
至于如何計(jì)算當(dāng)前頁(yè)面 DOM ??的分?jǐn)?shù),LightHouse 的源碼中會(huì)根據(jù)當(dāng)前節(jié)點(diǎn)深度作為變量做一個(gè)權(quán)重的計(jì)算,具體實(shí)現(xiàn)可以參考 LightHouse 源碼。
const?curNodeScore?=?1?+?0.5?*?depth;
const?domScore?=?所有子節(jié)點(diǎn)分?jǐn)?shù)求和
上述計(jì)算方式性能開(kāi)銷(xiāo)大且未必準(zhǔn)確,LightHouse 6.0 已明確廢棄了 FMP 打分項(xiàng),建議在具體業(yè)務(wù)場(chǎng)景中根據(jù)實(shí)際情況手動(dòng)埋點(diǎn)來(lái)確定 FMP 具體的值,更準(zhǔn)確也更高效。
LCP

沒(méi)錯(cuò),LCP (Largest Contentful Paint) 是就是用來(lái)代替 FMP 的一個(gè)性能指標(biāo)?,用于度量視口中最大的內(nèi)容元素何時(shí)可見(jiàn),可以用來(lái)確定頁(yè)面的主要內(nèi)容何時(shí)在屏幕上完成渲染。
使用 Largest Contentful Paint API 和 PerformanceObserver 即可獲取 LCP 指標(biāo)的值:

獲取 LCP
Interactivity 交互
TTI

TTI(Time To Interactive) 表示從頁(yè)面加載開(kāi)始到頁(yè)面處于完全可交互狀態(tài)所花費(fèi)的時(shí)間,?TTI 值越小,代表用戶可以更早地操作頁(yè)面,用戶體驗(yàn)就更好。
這里定義一下什么是完全可交互狀態(tài)的頁(yè)面:
頁(yè)面已經(jīng)顯示有用內(nèi)容。
頁(yè)面上的可見(jiàn)元素關(guān)聯(lián)的事件響應(yīng)函數(shù)已經(jīng)完成注冊(cè)。
事件響應(yīng)函數(shù)可以在事件發(fā)生后的 50ms 內(nèi)開(kāi)始執(zhí)行(主線程無(wú) Long Task)。
TTI 的算法略有些復(fù)雜,結(jié)合下圖看一下具體步驟:

TTI 示意圖
Long Task: 阻塞主線程達(dá) 50 毫秒或以上的任務(wù)。
從 FCP 時(shí)間開(kāi)始,向前搜索一個(gè)不小于 5s 的靜默窗口期。(靜默窗口期定義:窗口所對(duì)應(yīng)的時(shí)間內(nèi)沒(méi)有 Long Task,且進(jìn)行中的網(wǎng)絡(luò)請(qǐng)求數(shù)不超過(guò) 2 個(gè))
找到靜默窗口期后,從靜默窗口期向后搜索到最近的一個(gè) Long Task,Long Task 的結(jié)束時(shí)間即為 TTI。
如果一直找到 FCP 時(shí)刻仍然沒(méi)有找到 Long Task,以 FCP 時(shí)間作為 TTI。
其實(shí)現(xiàn)需要支持 Long Tasks API 和 Resource Timing API,具體實(shí)現(xiàn)感興趣的同學(xué)可以按照上述流程嘗試手動(dòng)實(shí)現(xiàn)。
FID

FID(First Input Delay) 用于度量用戶第一次與頁(yè)面交互的延遲時(shí)間,是用戶第一次與頁(yè)面交互到瀏覽器真正能夠開(kāi)始處理事件處理程序以響應(yīng)該交互的時(shí)間。
其實(shí)現(xiàn)使用簡(jiǎn)潔的 PerformanceEventTiming API 即可,回調(diào)的觸發(fā)時(shí)機(jī)是用戶首次與頁(yè)面發(fā)生交互并得到瀏覽器響應(yīng)(點(diǎn)擊鏈接、輸入文字等)。

獲取 FID
至于為何新的標(biāo)準(zhǔn)中采用 FID 而非 TTI,可能存在以下幾個(gè)因素:
FID 是需要用戶實(shí)際參與頁(yè)面交互的,只有用戶進(jìn)行了交互動(dòng)作才會(huì)上報(bào) FID,TTI 不需要。 FID?反映用戶對(duì)頁(yè)面交互性和響應(yīng)性的第一印象,良好的第一印象有助于用戶建立對(duì)整個(gè)應(yīng)用的良好印象。
Visual Stability 視覺(jué)穩(wěn)定
CLS

CLS(Cumulative Layout Shift) 是對(duì)在頁(yè)面的整個(gè)生命周期中發(fā)生的每一次意外布局變化的最大布局變化得分的度量,布局變化得分越小證明你的頁(yè)面越穩(wěn)定。
聽(tīng)起來(lái)有點(diǎn)復(fù)雜,這里做一個(gè)簡(jiǎn)單的解釋:
不穩(wěn)定元素:一個(gè)非用戶操作但發(fā)生較大偏移的可見(jiàn)元素稱為不穩(wěn)定元素。 布局變化得分:元素從原始位置偏移到當(dāng)前位置影響的頁(yè)面比例 * 元素偏移距離比例
舉個(gè)例子,一個(gè)占據(jù)頁(yè)面高度 50% 的元素,向下偏移了 25%,那么其得分為 0.75 * 0.25,大于標(biāo)準(zhǔn)定義的 0.1 分,該頁(yè)面就視為視覺(jué)上沒(méi)那么穩(wěn)定的頁(yè)面。

使用 Layout Instability API 和 PerformanceObserver 來(lái)獲取 CLS:

獲取 CLS
一點(diǎn)感受:在翻閱諸多參考資料后,私以為性能監(jiān)控是一件長(zhǎng)期實(shí)踐、以實(shí)際業(yè)務(wù)為導(dǎo)向的事情,業(yè)內(nèi)主流標(biāo)準(zhǔn)日新月異,到底監(jiān)控什么指標(biāo)是最貼合用戶體驗(yàn)的我們不得而知,對(duì)于 FMP、FPS 這類瀏覽器未提供 API 獲取方式的指標(biāo)花費(fèi)大量力氣去探索實(shí)現(xiàn)是否有足夠的收益也存在一定的疑問(wèn),但毋容置疑的是從自身頁(yè)面的業(yè)務(wù)屬性出發(fā),結(jié)合一些用戶反饋再進(jìn)行相關(guān)手段的優(yōu)化可能是更好的選擇。(更推薦深入了解瀏覽器渲染原理,寫(xiě)出性能極佳的頁(yè)面,讓 APM 同學(xué)失業(yè)
數(shù)據(jù)上報(bào)
得到所有錯(cuò)誤、性能、用戶行為以及相應(yīng)的環(huán)境信息后就要考慮如何進(jìn)行數(shù)據(jù)上報(bào),理論上正常使用ajax 即可,但有一些數(shù)據(jù)上報(bào)可能出現(xiàn)在頁(yè)面關(guān)閉 (unload) 的時(shí)刻,這些請(qǐng)求會(huì)被瀏覽器的策略 cancel 掉,因此出現(xiàn)了以下幾種解決方案:
優(yōu)先使用 Navigator.sendBeacon,這個(gè) API 就是為了解決上述問(wèn)題而誕生,它通過(guò) HTTP POST 將數(shù)據(jù)異步傳輸?shù)椒?wù)器且不會(huì)影響頁(yè)面卸載。
如果不支持上述 API,動(dòng)態(tài)創(chuàng)建一個(gè)??/?> 標(biāo)簽將數(shù)據(jù)通過(guò) url 拼接的方式傳遞。
使用同步 XHR?進(jìn)行上報(bào)以延遲頁(yè)面卸載,不過(guò)現(xiàn)在很多瀏覽器禁止了該行為。
Slardar 采取了第一種方式,不支持 sendBeacon 則使用 XHR,偶爾丟日志的原因找到了。
由于監(jiān)控?cái)?shù)據(jù)通常量級(jí)都十分龐大,因此不能簡(jiǎn)單地采集一個(gè)就上報(bào)一個(gè),需要一些優(yōu)化手段:
請(qǐng)求聚合:將多條數(shù)據(jù)聚合一次性上報(bào)可以減少請(qǐng)求數(shù)量,例如我們打開(kāi)任意一個(gè)已接入 Slardar 的頁(yè)面查看? batch 請(qǐng)求的請(qǐng)求體:

設(shè)置采樣率:?像崩潰、異常這類數(shù)據(jù)不出意外都是設(shè)置 100% 的采樣率,對(duì)于自定義日志可以設(shè)置一個(gè)采樣率來(lái)減少請(qǐng)求數(shù)量,大致實(shí)現(xiàn)思路如下:

總結(jié)
本文旨在提供一個(gè)相對(duì)體系的前端監(jiān)控視圖,幫助各位了解前端監(jiān)控領(lǐng)域我們能做什么、需要做什么。此外,如果能對(duì)頁(yè)面性能和異常處理有著更深入的認(rèn)知,無(wú)論是在開(kāi)發(fā)應(yīng)用時(shí)的自我管理(減少 bug、有意識(shí)地書(shū)寫(xiě)高性能代碼),還是自研監(jiān)控 SDK 都有所裨益。
如何設(shè)計(jì)監(jiān)控 SDK 不是本文的重點(diǎn),部分監(jiān)控指標(biāo)的定義和實(shí)現(xiàn)細(xì)節(jié)也可能存在其他解法,實(shí)現(xiàn)一個(gè)完善且健壯的前端監(jiān)控 SDK 還有很多技術(shù)細(xì)節(jié),例如每個(gè)指標(biāo)可以提供哪些配置項(xiàng)、如何設(shè)計(jì)上報(bào)的維度、如何做好兼容性等等,這些都需要在真實(shí)的業(yè)務(wù)場(chǎng)景中不斷打磨和優(yōu)化才能趨于成熟。
參考
Google Developer
我組建了技術(shù)交流群,里面有很多?大佬,歡迎進(jìn)來(lái)交流、學(xué)習(xí)、共建。回復(fù)?加群?即可。有超多大廠內(nèi)推機(jī)會(huì)。后臺(tái)回復(fù)「電子書(shū)」即可免費(fèi)獲取?27本?精選的前端電子書(shū)!
???“分享、點(diǎn)贊、在看” 支持一波??
