前端監(jiān)控SDK開發(fā)分享
大廠技術(shù)??高級前端??Node進階
點擊上方?程序員成長指北,關(guān)注公眾號
回復(fù)1,加入高級Node交流群
作者:子慕大詩人
原文:https://www.cnblogs.com/1wen/p/14417475.html
目錄
前言
收集哪些數(shù)據(jù)
性能
錯誤
輔助信息
小結(jié)
客戶端SDK(探針)相關(guān)原理和API
Web
微信小程序
編寫測試用例
單元測試
流程測試
- 提供Web環(huán)境的方式
-?Mock Web API的方式
結(jié)語
一、前言
隨著前端的發(fā)展和被重視,慢慢的行業(yè)內(nèi)對于前端監(jiān)控系統(tǒng)的重視程度也在增加。這里不對為什么需要監(jiān)控再做解釋。那我們先直接說說需求。
對于中小型公司來說,可以直接使用三方的監(jiān)控,比如自己搭建一套免費的sentry就可以捕獲異常和上報事件,或者使用阿里云的ARMS,功能比較全面也并不會太貴。類似的開源系統(tǒng)或者付費系統(tǒng)還很多,都能滿足我們一定的需求。
假如這個公司逐漸成長,已經(jīng)成為一個中大型的公司,用戶量、業(yè)務(wù)服務(wù)、公司整體架構(gòu)全部都在升級,這樣三方的監(jiān)控系統(tǒng)可能就慢慢的出現(xiàn)一些不能滿足需求的問題。比如企業(yè)內(nèi)部各種系統(tǒng)之間的關(guān)系太獨立和分散,不能使用內(nèi)部的統(tǒng)一登陸、不能相互跳轉(zhuǎn),想要增加一些字段收集并不能很快得到支持等等。這些問題都會導(dǎo)致效率上不能滿足企業(yè)發(fā)展要求。一個內(nèi)部可控并且能高速響應(yīng)企業(yè)需求的前端監(jiān)控系統(tǒng)就顯得很有必要。
我們在內(nèi)部的前端監(jiān)控系統(tǒng)上已經(jīng)投入了一定的精力和時間,今天分享一下前端監(jiān)控SDK部分的內(nèi)容,主要三個方面:
收集哪些數(shù)據(jù)
客戶端SDK(探針)及原理
編寫測試用例
二、收集哪些數(shù)據(jù)
前端監(jiān)控系統(tǒng)最核心的首要是收集客戶端的相關(guān)數(shù)據(jù),我們現(xiàn)在支持的客戶端探針有:web、微信小程序、andriod和ios。它們主要收集如圖以下信息:

2.1 性能
收集頁面加載、靜態(tài)資源、ajax接口等性能信息,指標(biāo)有加載時間、http協(xié)議版本、響應(yīng)體大小等,這是為業(yè)務(wù)整體質(zhì)量提升提供數(shù)據(jù)支撐,解決慢查詢問題等。
2.2 錯誤
收集js報錯、靜態(tài)資源加載錯誤、ajax接口加載錯誤,這些常規(guī)錯誤收集都很好理解。下面主要說明一下"業(yè)務(wù)接口錯誤(bussiness)":
客戶端發(fā)送ajax請求后端業(yè)務(wù)接口,接口都會返回json數(shù)據(jù)結(jié)構(gòu),而其中一般都會有errorcode和message兩個字段,errorcode為業(yè)務(wù)接口內(nèi)部定義的狀態(tài)碼。正常的業(yè)務(wù)響應(yīng)內(nèi)部都會約定比如errorcode==0等,那如果不為0可能是一些異常問題或者可預(yù)見的異常問題,這種錯誤數(shù)據(jù)就是需要收集的。
由于不同團隊或者接口可能約定都不一樣,所以我們只會提供一個預(yù)設(shè)方法,預(yù)設(shè)方法會在ajax請求響應(yīng)后調(diào)用,業(yè)務(wù)方自己根據(jù)約定和響應(yīng)的json數(shù)據(jù),在預(yù)設(shè)的方法中編寫判斷邏輯控制是否上報。像是下面這樣:
errcodeReport(res) {
if (Object.prototype.toString.call(res) === '[object Object]' && res.hasOwnProperty('errcode') && res.errcode !== 0) {
return { isReport: true, errMsg: res.errmsg,code: res.errcode };
}
return { isReport: false };
}
2.3 輔助信息
除了上面兩類硬指標(biāo)數(shù)據(jù),我們還需要很多其它的信息,比如:用戶的訪問軌跡、用戶點擊行為、用戶ID、設(shè)備版本、設(shè)備型號、UV/UA標(biāo)識、traceId等等。很多時候我們要解決的問題并不是那么簡單直接就能排查出來,甚至我們需要前端監(jiān)控和其它系統(tǒng)在某些情況下能夠關(guān)聯(lián)上,所以這些軟指標(biāo)信息同樣很重要。
在這里專門解釋一下traceId:
現(xiàn)在的后端服務(wù)都會使用APM(應(yīng)用性能管理)系統(tǒng),APM工具會在一次完整請求調(diào)用之初生成唯一的id,通常叫做traceId,它會記錄整個請求過程服務(wù)端的鏈路細(xì)節(jié)。如果前端能夠獲取到它,就能通過它去后端APM系統(tǒng)中查詢某次請求的日志信息。只要后端做好相關(guān)的配置,后端接口在響應(yīng)客戶端http請求時,可以把traceId返回給客戶端,SDK便可以去收集ajax請求的traceId,這樣前后端監(jiān)控就能夠關(guān)聯(lián)上了。
2.4 小結(jié)
收集以上的信息并開發(fā)一套管理臺,能夠達到監(jiān)控前端性能和異常錯誤的目的。想象一個場景,當(dāng)我們收到監(jiān)控系統(tǒng)的告警或者相關(guān)同事的問題反饋時,我們能打開管理臺,首先查看到實時的錯誤,如果發(fā)現(xiàn)是js的代碼導(dǎo)致的問題,我們能很快找到前端代碼錯誤的地方。如果不是前端的錯誤,我們通過收集的業(yè)務(wù)接口錯誤發(fā)現(xiàn)是后端接口的問題,我們也能及時的通知后端同事,在什么時間哪個接口報出errorcode為xx的錯誤,并且我們還能通過traceId直接查到這次ajax請求的后端鏈路監(jiān)控數(shù)據(jù)。如果實在不是明顯就能排查到的問題,我們還能通過收集到的用戶軌跡、設(shè)備信息和網(wǎng)絡(luò)請求等數(shù)據(jù),多方面的分析還原用戶當(dāng)時的場景,來輔助我們排查代碼中的難以復(fù)現(xiàn)的bug或者兼容問題。
在以上這個場景中,我們能夠提高前端排查問題的能力,甚至能輔助后端同學(xué)。在大部分時候,出現(xiàn)bug,很可能第一時間首先是找到前端做反饋,前端是排查問題的先頭部隊。當(dāng)我們有這樣的前端監(jiān)控系統(tǒng)之后,不至于每次遇到問題手足無措,解決問題的時間也會快許多。
【具體字段一覽】

確定好了要收集哪些信息,接下來就需要去實現(xiàn)客戶端SDK,它能夠在業(yè)務(wù)項目中自動收集數(shù)據(jù)上報給服務(wù)端。
三、客戶端SDK(探針)相關(guān)原理和API
所謂探針,是因為我們的SDK要依托于監(jiān)控的前端項目的運行環(huán)境,在其運行環(huán)境的底層API中加入探針函數(shù)來收集信息,下面分享WEB和微信小程序SDK實現(xiàn)的主要原理和使用的API。
3.1 WEB
下圖是SDK主要使用的Web API,通過這幾個API我們就能分別獲取到:頁面性能信息、資源性能信息、ajax信息、錯誤信息。

3.1.1 Performance
通過performance.timing可以拿到頁面首次加載的性能數(shù)據(jù),dns、tcp、白屏?xí)r間等,而在最新的標(biāo)準(zhǔn)中performance.timing已經(jīng)被廢棄,因此我們也改造為使用performance.getEntriesByType('navigation')。這里的白屏?xí)r間可能和實際真正的用戶感官的白屏?xí)r間是有差異的,僅供參考。
通過new PerformanceObserver監(jiān)聽器,我們可以監(jiān)聽所有資源(css,script,img,ajax等)加載的性能數(shù)據(jù):加載時間,響應(yīng)大小,http協(xié)議版本(http1.1/http2)等。而后我們需要通過一個數(shù)組去管理資源性能數(shù)據(jù),在完成數(shù)據(jù)上報后,清空數(shù)組。
3.1.2 fetch/xmlHttpRequest
由于瀏覽器并沒有提供一個統(tǒng)一的API使我們能夠收集到ajax請求和響應(yīng)數(shù)據(jù),并且不管我們是用axois還是使用其他的http請求庫,他們都是基于fetch和xmlHttpRequest實現(xiàn)的。因此只能通過重寫fetch和xmlHttpRequest,并在對應(yīng)的函數(shù)和邏輯中插入自定義代碼,來達到收集的目的。相關(guān)的文章很多,這里就不再細(xì)說了。
let _fetch = fetch;
window.fetch = function () {
// custom code
return _fetch
.apply(this, arguments)
.then((res) => {
// custom code
return res;
})
};
3.1.3 window.onerror | unhandledrejection | console.error | 以及框架自帶的監(jiān)聽函數(shù)
最后這幾個API都是收集js相關(guān)錯誤信息的。需要注意兩個問題:
一是onerror會獲取不到跨域的script錯誤,解決方案也很簡單:為跨域的script標(biāo)簽設(shè)置crossorigin屬性,并且需要靜態(tài)服務(wù)器為當(dāng)前資源設(shè)置CORS響應(yīng)頭。
二是代碼壓縮后的報錯信息需要通過sourceMap文件解析出源代碼對應(yīng)的行列和錯誤信息,sourceMap本身是一種數(shù)據(jù)結(jié)構(gòu),存儲了源代碼和壓縮代碼的關(guān)系數(shù)據(jù),通過解析庫能夠很輕松轉(zhuǎn)換它們。但如何自動化管理和操作sourceMap文件才是前端監(jiān)控系統(tǒng)核心需要解決的問題。這里就需要結(jié)合企業(yè)內(nèi)部的靜態(tài)資源發(fā)布系統(tǒng)和前端監(jiān)控系統(tǒng),來解決低效率的手動打包上傳問題。
3.2 微信小程序
微信小程序底層使用js實現(xiàn),有著它自己的一套生命周期,也提供了全局的API。通過重寫它的部分全局函數(shù)和相關(guān)API我們能獲取到:網(wǎng)絡(luò)請求、錯誤信息、設(shè)備和版本信息等。由于微信小程序的加載流程是由微信APP控制的,js等資源也被微信內(nèi)部托管,因此和web不同,我們沒有辦法獲取到web中performance能獲取到的頁面和資源加載信息。下圖是SDK主要使用的API

3.2.1 App和Component
通過重寫全局的App函數(shù),綁定onError方法監(jiān)聽錯誤,重寫它的onShow方法執(zhí)行小程序啟動時SDK需要的邏輯。通過重寫Component的onShow方法,可以在頁面組件切換時執(zhí)行我們的路徑收集和執(zhí)行上報等邏輯。
// SDK初始化函數(shù)
init(){
this.appMethod = App;
this.componentMethod = Component;
const ctx = this;
//重寫微信小程序Component
Component = (opts) => {
overrideComponent(opts, ctx);
ctx.componentMethod(opts);
};
//重寫微信小程序App
App = (app) => {
overrideApp(app, ctx);
ctx.appMethod(app);
};
}
//注意ctx是sdk的this
overrideComponent(opts, ctx) => {
const compOnShow = opts.methods.onShow;
opts.methods.onShow = function(){
// do something
//注意這里的this是實際調(diào)用方
compOnShow.apply(this, arguments)
}
})
overrideApp(app, ctx) => {
const _onError = app.onError || function () {};
const _onShow = app.onShow || function () {};
app.onError = function (err) {
reportError(err, ctx);
return _onError.apply(this, arguments);
};
app.onShow = function () {
//do something
return _onShow.apply(this, arguments);
};
})
3.2.2 重寫wx.request
這里也是因為和?fetch/xmlHttpRequest?一樣,并沒有一個全局的API能讓我們捕獲到請求信息,因此只能通過重寫wx.request來達到監(jiān)聽收集的功能。
const originRequest = wx.request;
const ctx = this;
//重寫wx.request,增加中間邏輯
Object.defineProperty(wx, 'request', {
value: function () {
// sdk code
const _complete = config.complete || function (data) {};
config.complete = function (data) {
// sdk code
return _complete.apply(this, arguments);
};
return originRequest.apply(this, arguments);
}
})
當(dāng)我們已經(jīng)實現(xiàn)了SDK之后或者說在實現(xiàn)的過程中,就需要編寫測試代碼了,下面說說編寫測試用例。
四、編寫測試用例
SDK屬于一個需要長期維護和更新的獨立庫,它被使用在很多業(yè)務(wù)項目中,要求更加穩(wěn)定,當(dāng)出現(xiàn)問題的時候,它的更新成本很高。需要經(jīng)歷:更新代碼->發(fā)布新版本->業(yè)務(wù)方更新依賴版本,等流程,而如果在這個流程中,假如SDK又改出其它問題,那將會再啟上述循環(huán),業(yè)務(wù)同事肯定會被麻煩死。隨著接入監(jiān)控的系統(tǒng)增多,在迭代過程中改動任何的代碼已經(jīng)讓人開始發(fā)慌,因為存在很多流程性的關(guān)聯(lián)邏輯,害怕改出問題。在一次代碼的重構(gòu)和優(yōu)化過程中,決心完善單元測試和流程測試。
4.1 單元測試
單元測試主要是對一些有明顯輸入輸出的通用方法,比如SDK的utils中的常用方法,SDK的參數(shù)配置方法等。而對于監(jiān)控SDK來說,更多的測試代碼主要集中在流程測試,對于單元測試這里就不具體說明了。
4.2 流程測試
監(jiān)控SDK在業(yè)務(wù)項目中初始化之后,主要是通過加入探針監(jiān)聽業(yè)務(wù)項目的運行狀態(tài)而收集信息并進行上傳的,它在大部分情況下并不是業(yè)務(wù)方調(diào)用什么就執(zhí)行什么。比如我們頁面初次加載,SDK在合適的時機會執(zhí)行首次加載相關(guān)信息的收集并上傳,那我們需要通過測試代碼來模擬這個流程,保障上報的數(shù)據(jù)是預(yù)期的。
我們的SDK運行在瀏覽器環(huán)境中,在node環(huán)境下是不支持Web相關(guān)API的。因此我們需要讓我們的測試代碼在瀏覽器中運行,或者提供相關(guān)API的支持。下面我們將會介紹兩種不同的方式,來支持我們的測試代碼正常運行。
4.2.1 提供Web環(huán)境的方式
假如我們使用mocha或者jest作為測試框架,可以通過mocha自帶的mocha.run方法在html中編寫和執(zhí)行我們的測試代碼,并在瀏覽器中打開運行;jest-lite也可以支持讓jest運行在瀏覽器中。
但有時候我們不想讓它打開瀏覽器,希望在終端中就能完成測試代碼運行,可以使用無頭瀏覽器,在node中加載瀏覽器環(huán)境,比如phontomjs或者puppeteer。他們提供了相關(guān)的工具,比如mocha-phantomjs就能直接在終端中運行html執(zhí)行測試流程。
基于寫好的html測試文件,再使用mocha-phantomjs和phantomjs,以下是package.json的命令配置。
scripts:{
test: mocha-phantomjs -p ./node_modules/.bin/phantomjs /test/unit/index.html
}
phontomjs已經(jīng)被廢棄了,不被推薦使用。推薦puppeteer,相關(guān)的功能和類似工具都有支持。
舉例說明:
以前有在WebSocket的代碼庫中使用過這種方式。因為依賴Web Api:?WebSocket。需要通過new WebSocket(),來完成測試流程,而node環(huán)境下沒有此API。于是使用mocha在html中寫測試用例,如果希望全程使用終端跑測試,還可以配合使用mocha-phantomjs讓測試的html文件可以在終端中執(zhí)行而不用打開本地的網(wǎng)頁運行。
當(dāng)然其實完全可以直接在瀏覽器中打開html查看測試運行結(jié)果,而且phantomjs相關(guān)的依賴包非常大、安裝也比較慢。但當(dāng)時我們使用了持續(xù)繼承服務(wù)travis,當(dāng)我們的代碼更新到遠(yuǎn)程倉庫以后,travis將會啟動多個獨立容器并在終端中執(zhí)行我們的測試文件,如果不使用mocha-phantomjs在終端中跑測試沒有辦法在travis中成功通過。
4.2.2 Mock Web API的方式
在這次完善監(jiān)控SDK測試的過程中,嘗試了另一種方式,全程使用Mock的方式。
上面的Web環(huán)境運行方式需要提供瀏覽器或者無頭瀏覽器。但實際我們需要測試的代碼并不是Web API,我們只是使用了它們。我們假定它們是穩(wěn)定的,我們只需要在乎它的輸入輸出,如果它們內(nèi)部出bug了,我們也是不能控制的,那是瀏覽器開發(fā)商的事情。因此我要做的事情僅僅是在node環(huán)境中模擬相關(guān)的Web API。
拿前面說到的WebSocket舉例,因為node中不支持WebSocket,我們沒有辦法new WebSocket。那假如有完全模擬WebSocket的三方node庫,我們就可以在node代碼中,直接讓執(zhí)行環(huán)境支持WebSocket:?const WebSocket = require('WebSocket')。這樣我們就不需要在瀏覽器或者無頭瀏覽器環(huán)境下運行了。
下面就具體拿我們的監(jiān)控SDK中的fetch舉例,是如何模擬流程測試的,總的來說要支持下面3個內(nèi)容,
啟動一個httpserver服務(wù)提供接口服務(wù)
引入三方庫,讓node支持fetch
node中手動模擬部分performance API

首先說明一下SDK中fetch的正常流程,當(dāng)我們的SDK在業(yè)務(wù)項目中初始化了之后,SDK會重寫fetch,于是業(yè)務(wù)項目中真正使用fetch做業(yè)務(wù)接口請求的時候,SDK就能通過之前重寫的邏輯獲取到http請求和響應(yīng)信息,同時也會通過performance獲取到fetch請求的性能信息,并進行上報。我們要寫的測試代碼,就是驗證這個流程能夠順利完成。
(1)http server
因為是驗證fetch完整流程,我們需要啟動一個httpserver服務(wù),提供接口來接收和響應(yīng)這次fetch請求。
(2)mock fetch
node環(huán)境中支持fetch的話,我們可以直接使用三方庫node-fetch,在執(zhí)行環(huán)境的頂部,我們就可以提前定義fetch。
/** MockFetch.js */
import fetch from 'node-fetch';
window = {};
window.fetch = fetch;
global.fetch = fetch;
(3)mock performance
而performance就比較特殊一點,沒有一個三方的庫能夠支持。對于fetch流程來說,我們?nèi)绻Mperformance,只需要模擬我們使用的PerformanceObserver,甚至一些入?yún)⒑头祷匚覀円部梢灾荒M我們需要的。下面的代碼是PerformanceObserver的使用例子。在SDK中,我們主要也是使用這一段代碼。
/** PerformanceObserver 使用實例 */
var observer = new PerformanceObserver(function(list, obj) {
var entries = list.getEntriesByType('resource');
for (var i=0; i < entries.length; i++) {
// Process "resource" events
}
});
observer.observe({entryTypes: ['resource']});
在瀏覽器內(nèi)部performance底層會自動去監(jiān)聽資源請求,我們只是通過它提供PerformanceObserver去收集它的數(shù)據(jù)。本質(zhì)上來說,主動收集的行為探針在performance內(nèi)部實現(xiàn)。
下面我們模擬PerformanceObserver一部分功能,來支持我們需要的測試流程。定義window.PerformanceObserver為構(gòu)造函數(shù),把傳入方法參數(shù)fn加入到數(shù)組中。mockPerformanceEntriesAdd?是我們需要手動調(diào)用的方法,當(dāng)我們發(fā)起一次fetch,我們就手動調(diào)用一下此方法,把mock數(shù)據(jù)傳入給注冊的監(jiān)聽函數(shù),這樣就能使PerformanceObserver的實例接收到我們的mock數(shù)據(jù),以此來模擬瀏覽器中performance內(nèi)部的行為。
/** MockPerformance.js */
let observerCallbacks = [];
//模擬PerformanceObserver對象,添加資源監(jiān)聽隊列
window.PerformanceObserver = function (fn) {
this.observe = function () {};
observerCallbacks.push(fn);
};
//手動觸發(fā)模擬performance資源隊列
window.mockPerformanceEntriesAdd = (resource) => {
observerCallbacks.forEach((cb) => {
cb({
getEntriesByType() {
return [resource];
},
});
});
};
通俗點舉例來說,十號公司要給打工人銀行卡發(fā)工資的,打工人的工資銀行卡第二天就會被扣房貸。打工人最關(guān)心的保障正??鄯抠J否則影響征信。本來打工人只需要關(guān)注銀行是否成功完成扣款,但是打工人最近丟工作了公司不會打款到工資卡,所以只能拿積蓄卡給自己的扣貸銀行卡轉(zhuǎn)錢,讓后續(xù)銀行可以扣錢還房貸。公司就是瀏覽器performance底層,打工人給自己轉(zhuǎn)錢就是mockPerformanceEntriesAdd,把公司發(fā)工資到銀行卡替換為自己轉(zhuǎn)錢進去,從被動接收變?yōu)橹鲃訄?zhí)行。細(xì)品,你細(xì)品~
mockPerformanceEntriesAdd就是模擬瀏覽器的主動行為,入?yún)⑹切阅苄畔?,我們可以直接寫死(下?code style="box-sizing: border-box;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;font-size: 0.87em;word-break: break-word;border-radius: 2px;overflow-x: auto;background-color: rgb(255, 245, 245);color: rgb(255, 80, 44);padding: 0.065em 0.4em;">mockData)??纯礈y試代碼
/** test/fetch.js */
import 'MockFetch.js';
import 'MockPerformance.js';
import webReportSdk from '../dist/monitorSDK';
//初始化監(jiān)控sdk,sdk內(nèi)部會重寫fetch
const monitor = webReportSdk({
appId: 'appid_test',
});
const mockData = {
name: 'http://localhost:xx/api/getData',
entryType: 'resource',
startTime: 90427.23999964073,
duration: 272.06500014290214,
initiatorType: 'fetch',
nextHopProtocol: 'h2',
...
}
test('web api: fetch', () => {
//GET
const requestAddress = mockData.name;
fetch(requestAddress, {
method: 'GET',
});
//發(fā)送請求后,需要模擬瀏覽器performace數(shù)據(jù)監(jiān)聽
window.mockPerformanceEntriesAdd(mockData);
})
當(dāng)mockPerformanceEntriesAdd執(zhí)行的時候,SDK內(nèi)部的PerformanceObserver便能收集到mock的性能信息了。( 這里注意,我們還需要啟動一個httpserver的服務(wù),服務(wù)提供http://localhost:xx/api/getData接口 )
當(dāng)上面的測試代碼運行的時候,SDK能夠獲取地址為http://localhost:xx/api/getData的fetch的請求、響應(yīng)和性能信息,并且SDK也會發(fā)送一次fetch請求把收集的數(shù)據(jù)上報給后端服務(wù)。我們可以再次重寫window.fetch,來攔截SDK的上報請求,就可以獲取到請求內(nèi)容,用請求內(nèi)容來做預(yù)期測試判斷
//再次重寫fetch,攔截請求并跳過上報
const monitorFetch = window.fetch;
let reportData;
window.fetch = function () {
//sdk上報的數(shù)據(jù)我們會做一個type標(biāo)記,避免SDK收集它自己發(fā)出的請求信息
if (arguments[1] && arguments[1].type === 'report-data') {
//獲取請求內(nèi)容
reportData = JSON.parse(arguments[1].body);
return Promise.resolve();
}
return monitorFetch.apply(this, arguments);
};
//省略中間代碼
expect(reportData.resourceList[0].name).toEqual(mockData.name);
合并后的測試代碼
/** test/fetch.js */
import 'MockFetch.js';
import 'MockPerformance.js';
import webReportSdk from '../dist/monitorSDK';
//初始化監(jiān)控sdk,sdk內(nèi)部會重寫fetch
const monitor = webReportSdk({
appId: 'appid_test',
});
//再次重寫fetch,攔截請求并跳過上報
const monitorFetch = window.fetch;
let reportData;
window.fetch = function () {
//sdk上報的數(shù)據(jù)我們會做一個type標(biāo)記,避免SDK收集它自己發(fā)出的請求信息
if (arguments[1] && arguments[1].type === 'report-data') {
//獲取請求內(nèi)容
reportData = JSON.parse(arguments[1].body);
return Promise.resolve();
}
return monitorFetch.apply(this, arguments);
};
const mockData = {
name: 'xxx.com/api/getData',
entryType: 'resource',
startTime: 90427.23999964073,
duration: 272.06500014290214,
initiatorType: 'fetch',
nextHopProtocol: 'h2',
...
}
test('web api: fetch', (done) => {
//GET
const requestAddress = mockData.name;
fetch(requestAddress, {
method: 'GET',
});
//發(fā)送請求后,需要模擬瀏覽器performace數(shù)據(jù)監(jiān)聽
window.mockPerformanceEntriesAdd(mockData);
//需要一定延遲
setTimeout(()=>{
expect(reportData.resourceList[0].name).toEqual(mockData.name);
//more expect...
done()
},3000)
})

如上圖所示,我們主要是以這樣的模式進行SDK的流程測試和代碼編寫。有了測試代碼后,能夠在很大程度上保障代碼維護迭代過程中的穩(wěn)定性可控性,也能省去很多后期測試成本。
五、結(jié)語
以上分享是我們在做監(jiān)控SDK時比較核心的這三個方面,還有很多其它的細(xì)節(jié)和實現(xiàn),比如:如何節(jié)流、上報時機、數(shù)據(jù)合并、初始化配置等。開發(fā)迭代過程中,要避免客戶端SDK或者后端服務(wù)因為迭代造成的兼容性問題。還比較重要的是要考慮后期數(shù)據(jù)庫查詢和存儲方面的需求,收集、存儲和查詢才能完整的構(gòu)成這套前端監(jiān)控系統(tǒng)。
- End -
Node 社群
我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。
如果你覺得這篇內(nèi)容對你有幫助,我想請你幫我2個小忙:
1. 點個「在看」,讓更多人也能看到這篇文章 2. 訂閱官方博客?www.inode.club?讓我們一起成長 點贊和在看就是最大的支持??
