前言相關(guān)學(xué)習(xí)推薦:javascript視頻教程
成都創(chuàng)新互聯(lián)公司長(zhǎng)期為上1000+客戶(hù)提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為惠濟(jì)企業(yè)提供專(zhuān)業(yè)的網(wǎng)站設(shè)計(jì)、網(wǎng)站制作,惠濟(jì)網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
最近雜七雜八的事情比較多,難得抽出時(shí)間來(lái)彌補(bǔ)一下之前的系列,欠大家的埋點(diǎn)系列現(xiàn)在開(kāi)始走起來(lái)
為什么需要埋點(diǎn)系統(tǒng)電影中前端開(kāi)發(fā)攻城獅開(kāi)開(kāi)心心的 coding,非常自豪的進(jìn)行了業(yè)務(wù)、UI 分離開(kāi)發(fā),各種設(shè)計(jì)模式、算法優(yōu)化輪番上陣,代碼寫(xiě)的 Perfect(勞資代碼天下第一),沒(méi)有 BUG,程序完美,兼容性 No.1,代碼能打能抗質(zhì)量高。下班輕松打卡,回家看娃。
現(xiàn)實(shí)中實(shí)際上,開(kāi)發(fā)環(huán)境與生產(chǎn)環(huán)境并不能等同,并且測(cè)試的過(guò)程再完善,依然會(huì)有漏測(cè)的情況存在??紤]到用戶(hù)使用客戶(hù)端環(huán)境、網(wǎng)絡(luò)環(huán)境等等一系列的不確定因素存在。
所以在開(kāi)發(fā)過(guò)程中一定要記得三大原則(我胡謅的)
沒(méi)有完美的代碼,只有沒(méi)發(fā)現(xiàn)的 BUG絕對(duì)不要相信測(cè)試環(huán)境,沒(méi)有一種測(cè)試環(huán)境都涵蓋所有線上情況如果線上沒(méi)有一點(diǎn)反饋,不要懷疑,問(wèn)題應(yīng)該藏得很深、很深什么是埋點(diǎn)系統(tǒng)埋點(diǎn)就像城市中的攝像頭,從產(chǎn)品的角度考慮,它可以監(jiān)控到用戶(hù)在我們產(chǎn)品里的行為軌跡,為產(chǎn)品的迭代、項(xiàng)目的穩(wěn)定提供依據(jù),WHO、WHEN、WHERE、HOW、WHAT 是埋點(diǎn)采集數(shù)據(jù)的基礎(chǔ)維度。
對(duì)前端開(kāi)發(fā)而言,可以監(jiān)控頁(yè)面資源加載性能,異常等等,提供了頁(yè)面體驗(yàn)和健康指數(shù),為后續(xù)性能優(yōu)化提供依據(jù),及時(shí)上報(bào)異常和發(fā)生場(chǎng)景。從而能夠及時(shí)修正問(wèn)題,提高項(xiàng)目質(zhì)量等。
埋點(diǎn)可以大概分為三類(lèi):
無(wú)痕埋點(diǎn) - 無(wú)差別收集頁(yè)面所有信息包括頁(yè)面進(jìn)出、事件點(diǎn)擊等等,需要進(jìn)行數(shù)據(jù)沖洗才能獲取到有用信息可視化埋點(diǎn) - 根據(jù)生成的頁(yè)面結(jié)構(gòu)獲取特定點(diǎn)位,單獨(dú)埋點(diǎn)分析業(yè)務(wù)代碼手動(dòng)埋點(diǎn) - 根據(jù)具體復(fù)雜的業(yè)務(wù),除掉上述兩種不能涵蓋的地方進(jìn)行業(yè)務(wù)代碼埋點(diǎn)大部分情況,我們可以通過(guò)無(wú)痕埋點(diǎn)收集到所有的信息數(shù)據(jù),再配合可視化埋點(diǎn),能夠具體定位到某一個(gè)點(diǎn)位,這樣大部分的埋點(diǎn)信息都據(jù)此分析出來(lái)。
在特殊情況下,可以多加上業(yè)務(wù)代碼手動(dòng)埋點(diǎn),處理一下特別的場(chǎng)景(大部分情況是走強(qiáng)業(yè)務(wù)與正常的點(diǎn)擊,刷新事件無(wú)關(guān)需要上報(bào)的信息)
埋點(diǎn) SDK 開(kāi)發(fā)埋點(diǎn)數(shù)據(jù)收集分析事件基本數(shù)據(jù)事件發(fā)生時(shí)間發(fā)生時(shí)頁(yè)面信息快照頁(yè)面頁(yè)面 PV,UV用戶(hù)頁(yè)面停留時(shí)長(zhǎng)頁(yè)面跳轉(zhuǎn)事件頁(yè)面進(jìn)入后臺(tái)用戶(hù)離開(kāi)頁(yè)面用戶(hù)信息用戶(hù) uid用戶(hù)設(shè)備指紋設(shè)備信息ip定位用戶(hù)操作行為用戶(hù)點(diǎn)擊點(diǎn)擊目標(biāo)頁(yè)面 AJAX 請(qǐng)求請(qǐng)求成功請(qǐng)求失敗請(qǐng)求超時(shí)頁(yè)面報(bào)錯(cuò)資源加載報(bào)錯(cuò)JS 運(yùn)行報(bào)錯(cuò)資源加載新性能圖片腳本頁(yè)面加載性能上面的數(shù)據(jù)通過(guò) 3 個(gè)維度來(lái)定義埋點(diǎn)事件
·LEVEL
: 描述埋點(diǎn)數(shù)據(jù)的日志級(jí)別INFO
:一些用戶(hù)操作,請(qǐng)求成功,資源加載等等正常的數(shù)據(jù)記錄ERROR
: JS報(bào)錯(cuò),接口報(bào)錯(cuò)等等錯(cuò)誤類(lèi)型的數(shù)據(jù)記錄DEBUG
: 預(yù)留開(kāi)發(fā)人員通過(guò)手動(dòng)調(diào)用的方式回傳排除bug的數(shù)據(jù)記錄WARN
: 預(yù)留開(kāi)發(fā)人員通過(guò)手動(dòng)調(diào)用的方式回傳非正常用戶(hù)行為的的數(shù)據(jù)記錄CATEGORY
:描述埋點(diǎn)數(shù)據(jù)的分類(lèi)TRACK
: 埋點(diǎn)SDK對(duì)象的生命周期管理整個(gè)埋點(diǎn)數(shù)據(jù)。WILL_MOUNT
:sdk對(duì)象即將初始化加載,生成一個(gè)默認(rèn)ID,跟蹤全部相關(guān)事件DID_MOUNTED
:sdk對(duì)象初始化完成,主要獲取設(shè)備指紋等等的異步操作完成AJAX
: AJAX相關(guān)數(shù)據(jù)ERROR
:頁(yè)面中的異常相關(guān)數(shù)據(jù)PERFORMANCE
: 關(guān)于性能相關(guān)數(shù)據(jù)OPERATION
: 用戶(hù)操作相關(guān)數(shù)據(jù)EVENT_NAME
:具體的事件名稱(chēng)根據(jù)上述的維度,我們可以簡(jiǎn)單設(shè)計(jì)如下的架構(gòu)
代理請(qǐng)求根據(jù)上圖的架構(gòu),再進(jìn)行下面的具體代碼開(kāi)發(fā)
在瀏覽器中現(xiàn)在主要有 2 種請(qǐng)求方式,一個(gè)是 XMLHttpRequest
, 一個(gè)是 Fetch
。
function NewXHR() { var realXHR: any = new OldXHR(); // 代理模式里面有提到過(guò) realXHR.id = guid() const oldSend = realXHR.send; realXHR.send = function (body) { oldSend.call(this, body) //記錄埋點(diǎn) } realXHR.addEventListener('load', function () { //記錄埋點(diǎn) }, false); realXHR.addEventListener('abort', function () { //記錄埋點(diǎn) }, false); realXHR.addEventListener('error', function () { //記錄埋點(diǎn) }, false); realXHR.addEventListener('timeout', function () { //記錄埋點(diǎn) }, false); return realXHR; }復(fù)制代碼代理 Fetch
const oldFetch = window.fetch; function newFetch(url, init) { const fetchObj = { url: url, method: method, body: body, } ajaxEventTrigger.call(fetchObj, AJAX_START); return oldFetch.apply(this, arguments).then(function (response) { if (response.ok) { //記錄埋點(diǎn) } else { //上報(bào)錯(cuò)誤 } return response }).catch(function (error) { fetchObj.error = error //記錄埋點(diǎn) throw error }) }復(fù)制代碼監(jiān)聽(tīng)頁(yè)面的
PV
,UV
在進(jìn)入頁(yè)面時(shí),我們通過(guò)算法生成一個(gè)唯一 session id
,作為這次埋點(diǎn)行為的全局 id,上報(bào)用戶(hù) id,設(shè)備指紋,設(shè)備信息。在用戶(hù)未登錄的情況下,通過(guò)設(shè)備指紋來(lái)計(jì)算 UV
,通過(guò) session id
計(jì)算 PV
。
異常就是干擾程序的正常流程的不尋常事故
RUNTIME ERROR在JS
中可以通過(guò) window.onerror
和window.addEventListener('error', callback)
捕捉運(yùn)行時(shí)異常,一般使用window.onerror
,它兼容性更好。
window.onerror = function(message, url, lineno, columnNo, error) { const lowCashMessage = message.toLowerCase() if(lowCashMessage.indexOf('script error') > -1) { return } const detail = { url: url filename: filename, columnNo: columnNo, lineno: lineno, stack: error.stack, message: message } //記錄埋點(diǎn)}復(fù)制代碼Script Error
在這里我們過(guò)濾了 Script Error
, 它產(chǎn)生的原因主要是頁(yè)面中加載的第三方跨域腳本報(bào)錯(cuò),比如托管在第三方 CDN 中的 js
腳本。這類(lèi)問(wèn)題比較難以排查。解決的方法有:
CORS
(Cross Origin Resource Sharing,跨域資源共享),如下步驟<srcipt src="another domain/main.js" cossorigin="anonymous"></script>
修改Access-Control-Allow-Origin: * | 指定域名
使用 try catch
<script scr="crgt.js"></script> //加載crgt腳本,window.crgt = {getUser: () => string} try{ window.crgt.getUser(); }catch(error) { throw error // 輸出正確的錯(cuò)誤堆棧 }復(fù)制代碼Promise reject
js
在異步異常時(shí)無(wú)法通過(guò) onerror
方法捕獲 ,在 Promise 對(duì)象在 reject 時(shí),同時(shí)并沒(méi)有進(jìn)行處理時(shí)
會(huì)拋出一個(gè) unhandledrejection
的錯(cuò)誤,并不會(huì)被上述的方法所捕獲,所以需要添加單獨(dú)的處理事件。
window.addEventListener("unhandledrejection", event => { throw event.reason });復(fù)制代碼資源加載異常
在瀏覽器中,可以通過(guò) window.addEventListener('error', callback)
的方式監(jiān)聽(tīng)資源加載異常,比如 js
或者 css
腳本文件丟失。
window.addEventListener('error', (event) => { if (event.target instanceof HTMLElement) { const target = parseDom(event.target, ['src']); const detail = { target: target, path: parseXPath(target), } // 記錄埋點(diǎn) } }, true)復(fù)制代碼監(jiān)聽(tīng)用戶(hù)行為
通過(guò) addEventListener click
監(jiān)聽(tīng) click
事件
window.addEventListener('click', (event) => { //記錄埋點(diǎn)}, true)復(fù)制代碼
在這里通過(guò)組件的 displaName
來(lái)定位元素的位置,displaName
表示組件的文件目錄,比如 src/components/Form.js
文件導(dǎo)出的組件 FormItem
通過(guò) babel plugin
自動(dòng)添加屬性 @components/Form.FormItem
,或者使用者主動(dòng)給組件添加 static
屬性 displayName
。
window.addEventListener('hashchange', event => { const { oldURL, newURL } = event; const oldURLObj = url.parseUrl(oldURL); const newURLObj = url.parseUrl(newURL); const from = oldURLObj.hash && url.parseHash(oldURLObj.hash); const to = newURLObj.hash && url.parseHash(newURLObj.hash); if(!from && !to ) return; // 記錄埋點(diǎn)})復(fù)制代碼監(jiān)聽(tīng)頁(yè)面離開(kāi)
通過(guò) addEventListener beforeunload
監(jiān)聽(tīng)離開(kāi)頁(yè)面事件
window.addEventListener('beforeunload', (event) => { //記錄埋點(diǎn)})復(fù)制代碼SDK 架構(gòu)
class Observable { constructor(observer) { observer(this.emit) } emit = (data) => { this.listeners.forEach(listener => { listener(data) }) } listeners = []; subscribe = (listener) => { this.listeners.push(listeners); return () => { const index = this.listeners.indexOf(listener); if(index === -1) { return false } this.listeners.splice(index, 1); return true; } } }復(fù)制代碼
const clickObservable = new Observable((emit) => { window.addEventListener('click', emit) })復(fù)制代碼
然而在處理 ajax
,需要將多種數(shù)據(jù)組合在一起,需要進(jìn)行 merg 操作,則顯得沒(méi)有那么優(yōu)雅,也很難適應(yīng)后續(xù)復(fù)雜的數(shù)據(jù)流的操作。
const ajaxErrorObservable = new Observable((emit) => { window.addEventListener(AJAX_ERROR, emit) })const ajaxSuccessObservable = new Observable((emit) => { window.addEventListener(AJAX_SUCCESS, emit) })const ajaxTimeoutObservable = new Observable((emit) => { window.addEventListener(AJAX_TIMEOUT, emit) })復(fù)制代碼
可以選擇 RxJS 來(lái)優(yōu)化代碼
export const ajaxError$ = fromEvent(window, 'AJAX_ERROR', true)export const ajaxSuccess$ = fromEvent(window, 'AJAX_SUCCESS', true)export const ajaxTimeout$ = fromEvent(window, 'AJAX_TIMEOUT', true)復(fù)制代碼
ajaxError$.pipe( merge(ajaxSuccess$, ajaxTimeout$), map(data=> (data) => ({category: 'ajax', data; data})) subscribe(data => console.log(data))復(fù)制代碼
通過(guò) merge
, map
兩個(gè)操作符完成對(duì)數(shù)據(jù)的合并和處理。
core
event$
數(shù)據(jù)流合并snapshot
獲取當(dāng)前設(shè)備快照,例如url
,userID
,router
track
埋點(diǎn)類(lèi),組合數(shù)據(jù)流和日志。logger
logger
日志類(lèi)info
warn
debug
error
observable
ajax
beforeUpload
opeartion
routerChange
logger
track
參考www.alibabacloud.com/help/zh/doc…結(jié)尾自建埋點(diǎn)系統(tǒng)是一個(gè)需要前后端一起合作的事情,如果人力不足的情況下,建議使用第三方分析插件,例如 Sentry 就能足夠滿(mǎn)足大部分日常使用
但還是建議多了解,在第三方插件出現(xiàn)不能滿(mǎn)足業(yè)務(wù)需求的時(shí)候,可以頂上。
想了解更多編程學(xué)習(xí),敬請(qǐng)關(guān)注php培訓(xùn)欄目!
文章標(biāo)題:初探埋點(diǎn)系統(tǒng)
文章轉(zhuǎn)載:http://www.rwnh.cn/article4/cghpoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、網(wǎng)站策劃、網(wǎng)站建設(shè)、建站公司、服務(wù)器托管、移動(dòng)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)