前言
埋點,是網站分析的一種經常使用的數據採集方法。咱們主要用來採集用戶行爲數據(例如頁面訪問路徑,點擊了什麼元素)進行數據分析,從而讓運營同窗更加合理的安排運營計劃。如今市面上有不少第三方埋點服務商,百度統計,友盟,growingIO 等你們應該都不太陌生,大多狀況下你們都只是使用,最近我研究了下 web 埋點,你要不要了解下。html
現有埋點三大類型
用戶行爲分析是一個大系統,一個典型的數據平臺。由用戶數據採集,用戶行爲建模分析,可視化報表展現幾個模塊構成。現有的埋點採集方案能夠大體被分爲三種,手動埋點,可視化埋點,無埋點
- 手動埋點
手動代碼埋點比較常見,須要調用埋點的業務方在須要採集數據的地方調用埋點的方法。優勢是流量可控,業務方能夠根據須要在任意地點任意場景進行數據採集,採集信息也徹底由業務方來控制。這樣的有點也帶來了一些弊端,須要業務方來寫死方法,若是採集方案變了,業務方也須要從新修改代碼,從新發布。 - 可視化埋點
但是化埋點是近今年的埋點趨勢,不少大廠本身的數據埋點部門也都開始作這塊。優勢是業務方工做量少,缺點則是技術上推廣和實現起來有點難(業務方前端代碼規範是個大前提)。阿里的活動頁不少都是運營經過可視化的界面拖拽配置實現,這些活動控件元素都帶有惟一標識。經過埋點配置後臺,將元素與要採集事件關聯起來,能夠自動生成埋點代碼嵌入到頁面中。 - 無埋點
無埋點則是前端自動採集所有事件,上報埋點數據,由後端來過濾和計算出有用的數據,優勢是前端只要加載埋點腳本。缺點是流量和採集的數據過於龐大,服務器性能壓力山大,主流的 GrowingIO 就是這種實現方案。
咱們暫時放棄可視化埋點的實現,在 手動埋點
和 無埋點
上進行了嘗試,爲了便於描述,下文我會稱採集腳本爲 SDK。前端
思考幾個問題
埋點開發須要考慮不少內容,貫穿着不輕易動手寫代碼的原則,咱們在開發前先思考下面這幾個問題
- 咱們要採集什麼內容,進行哪些採集接口的約定
- 業務方經過什麼方式來調用咱們的採集腳本
- 手動埋點:SDK 須要封裝一個方法給業務方進行調用,傳參方式業務方可控
- 無埋點:考慮到數據量對於服務器的壓力,咱們須要對無埋點進行開關配置,能夠配置進行哪些元素進行無埋點採集
- 用戶標識:遊客用戶和登陸用戶的採集數據怎麼進行區分關聯
- 設備Id:用戶經過瀏覽器來訪問 web 頁面,設備Id須要存儲在瀏覽器上,同一個用戶訪問不一樣的業務方網站,設備Id要保持同樣,怎麼實現
- 單頁面應用:如今流行的單頁面應用和普通 web 頁面的數據採集是否有差別
- 混合應用:app 與 h5 的混合應用咱們要怎麼進行通信
咱們要採集什麼內容,進行哪些採集接口的約定
第一期咱們先實現對 PV(即頁面瀏覽量或點擊量) 、UV(一天內同個訪客屢次訪問) 、點擊量、用戶的訪問路徑的基礎指標的採集。精細化分析的流量轉化須要和業務相關,須要和數據分析方作約定,咱們預留擴展。因此咱們的採集接口須要進行如下的約定java
{
"header":{ // HTTP 頭部 "X-Device-Id":" 550e8400-e29b-41d4-a716-446655440000", //設備ID,用來區分用戶設備 "X-Source-Url":"https://www.baidu.com/", //源地址,關聯用戶的整個操做流程,用於用戶行爲路徑分析,例如登陸,到首頁,進入商品詳情,退出這一整個完整的路徑 "X-Current-Url":"", //當前地址,用戶行爲發生的頁面 "X-User-Id":"",//用戶ID,統計登陸用戶行爲 }, "body":[{ // HTTP Body體 "PageSessionID":"", //頁面標識ID,用來區分頁面事件,例如加載和離開咱們會發兩個事件,這個標識可讓咱們知道這個事件是發生在一個頁面上 "Event":"loaded", //事件類型,區分用戶行爲事件 "PageTitle": "埋點測試頁", //頁面標題,直觀看到用戶訪問頁面 "CurrentTime": 「1517798922201」, //事件發生的時間 "ExtraInfo": { } //擴展字段,對具體業務分析的傳參 }] }
以上就是咱們如今約定好了的通用的事件採集的接口,所傳的參數基本上會根據採集事件的不一樣而發生變化。可是在用戶的整一個訪問行爲中,用戶的設備是不會變化的,若是你想採集設備信息能夠從新約定一個接口,在整個採集開始以前發送設備信息,這樣能夠避免在事件採集接口上重複採集固定數據。android
{
"header":{ // HTTP 頭部 "X-Device-Id" :"550e8400-e29b-41d4-a716-446655440000" , // 設備id }, "body":{ // HTTP Body體 "DeviceType": "web" , //設備類型 "ScreenWide" : 768 , // 屏幕寬 "ScreenHigh": 1366 , // 屏幕高 "Language": "zh-cn" //語言 } }
業務方經過什麼方式來調用咱們的採集腳本
埋點應該讓調用的業務方,儘量少有工做量,最好是什麼都不用作,😁,可是實現起來有點難額。咱們採用的方案是讓業務方在代碼裏經過 script 腳原本引用咱們的 SDK ,業務方只要配置一些須要的參數進行埋點定製(👆咱們講到過的無埋點的流量控制),而後什麼都不作就能夠進行基礎數據的採集。ios
(function() { var collect = document.createElement('script'); collect.type = 'text/javascript'; collect.async = true; collect.src = 'http://collect.trc.com/index.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(collect, s); })(); //用戶自定義要進行無埋點採集的元素,若是不進行無埋點採集,能夠不配置 var _XT = []; _XT.push(['Target','div']);
手動埋點:SDK
若是業務方須要採集更多業務定製的數據,能夠調用咱們暴露出的方法進行採集git
//自定義事件 sdk.dispatch('customEvent',{extraInfo:'自定義事件的額外信息'})
遊客與用戶關聯
咱們使用 userId 來作用戶標識,同一個設備的用戶,從遊客用戶切換到登陸用戶,若是咱們要把他們關聯起來,須要有一個設備Id 作關聯github
web 設備Id
用戶經過瀏覽器來訪問 web 頁面,設備Id須要存儲在瀏覽器上,同一個用戶訪問不一樣的業務方網站,設備Id要保持同樣。web 變量存儲,咱們第一時間想到的就是 cookie,sessionStorage,localStorage,可是這3種存儲方式都和訪問資源的域名相關。咱們總不能每次訪問一個網站就新建一個設備指紋吧,因此咱們須要經過一個方法來跨域共享設備指紋web
咱們想到的方案是,經過嵌套 iframe 加載一個靜態頁面,在 iframe 上加載的域名上存儲設備id,經過跨域共享變量獲取設備id,共享變量的原理是採用了iframe 的 contentWindow通信,經過 postMessage 獲取事件狀態,調用封裝好的回調函數進行數據處理具體的實現方式typescript
//web 應用,經過嵌入 iframe 進行跨域 cookie 通信,設置設備id, collect.setIframe = function () { var that = this var iframe = document.createElement('iframe') iframe.id = "frame", iframe.src = 'http://collectiframe.trc.com' // 配置域名代理,目的是讓開發測試生產環境代碼一致 iframe.style.display='none' //iframe 設置的目的是用來生成固定的設備id,不展現 document.body.appendChild(iframe) iframe.onload = function () { iframe.contentWindow.postMessage('loaded','*'); } //監聽message事件,iframe 加載完成,獲取設備id ,進行相關的數據採集 helper.on(window,"message",function(event){ that.deviceId = event.data.deviceId if(event.data && event.data.type == 'loaded'){ that.sendDevice(that.getDevice(), that.deviceUrl); setTimeout(function () { that.send(that.beforeload) that.send(that.loaded) },1000) } }) }
iframe 與 SDK 通信
function receiveMessageFromIndex ( event ) { getDeviceInfo() // 獲取設備信息 var data = { deviceId: _deviceId, type:event.data } event.source.postMessage(data, '*'); // 將設備信息發送給 SDK } //監聽message事件 if(window.addEventListener){ window.addEventListener("message", receiveMessageFromIndex, false); }else{ window.attachEvent("onmessage", receiveMessageFromIndex, false)
若是你想知道能夠看個人另外一篇博客 web 瀏覽器指紋跨域共享
單頁面應用:如今流行的單頁面應用和普通 web 頁面的數據採集是否有差別
咱們知道單頁面應用都是無刷新的頁面加載,因此咱們在頁面
跳轉
的處理和咱們的普通的頁面會有所不一樣。單頁面應用的路由插件運用了 window 自帶的無刷新修改用戶瀏覽記錄的方法,pushState 和 replaceState。
window 的 history 對象 提供了兩個方法,可以無刷新的修改用戶的瀏覽記錄,pushSate,和 replaceState,區別的 pushState 在用戶訪問頁面後面添加一個訪問記錄, replaceState 則是直接替換了當前訪問記錄,因此咱們只要改寫 history 的方法,在方法執行前執行咱們的採集方法就能實現對單頁面應用的頁面跳轉事件的採集了
// 改寫思路:拷貝 window 默認的 replaceState 函數,重寫 history.replaceState 在方法裏插入咱們的採集行爲,在重寫的 replaceState 方法最後調用,window 默認的 replaceState 方法 collect = {} collect.onPushStateCallback : function(){} // 自定義的採集方法 (function(history){ var replaceState = history.replaceState; // 存儲原生 replaceState history.replaceState = function(state, param) { // 改寫 replaceState var url = arguments[2]; if (typeof collect.onPushStateCallback == "function") { collect.onPushStateCallback({state: state, param: param, url: url}); //自定義的採集行爲方法 } return replaceState.apply(history, arguments); // 調用原生的 replaceState }; })(window.history);
這塊介紹起來也比較的複雜,若是你想了解更多,能夠看個人另外一篇博客你須要知道的單頁面路由實現原理
混合應用:app 與 h5 的混合應用咱們要怎麼進行通信
如今大部分的應用都不是純原生的應用, app 與 h5 的混合的應用是如今的一種主流。
純 web 數據採集咱們考慮到前端存儲數據容易丟失,咱們在每一次事件觸發的時候都用採集接口傳輸採集到的數據。考慮到如今不少用戶的手機會有流量管家的軟件監控,若是在 App 中 h5 仍是採集到數據就傳輸給服務端,頗有可能會讓流量管家檢測到,給用戶報警,從而使得用戶再也不信任你的 App , 因此咱們在用戶操做的時候將數據傳給 app 端,存儲到 app。用戶切換應用到後臺的時候,經過 app 端的 SDK 打包傳輸到服務器,咱們給 app 提供的方法封裝了一個適配器
// app 與 h5 混合應用,直接將數信息發給 app collect.saveEvent = function (jsonString) { collect.dcpDeviceType && setTimeout(function () { if(collect.dcpDeviceType=='android'){ android.saveEvent(jsonString) } else { window.webkit && window.webkit.messageHandlers ? window.webkit.messageHandlers.nativeBridge.postMessage(jsonString) : window.postBridgeMessage(jsonString) } },1000) }
實現思路
經過上面幾個問題的思考,咱們對埋點的實現大體已經有了一些想法,咱們使用思惟導圖來還原下咱們即將要作的事情,圖片記得放大看哦,過小了可能看不清。
咱們須要暴露給業務方調用的方法
咱們須要處理的事件類型
SDK 的基本實現思路
咱們來看下幾個核心代碼的實現
工具方法
咱們定義了幾個工具方法,提升開發的幸福指數 😝
var helper = {}; // 生成一個惟一的標識,pageSessionId (用這個變量來關聯開始加載、加載完成、離開頁面的事件,計算出頁面加菜時間,停留時間) helper.uuid = function(){} // 元素綁定事件監聽,兼容瀏覽器到IE8 helper.on = function(){} //元素移除事件監聽的適配器函數,兼容瀏覽器到IE8 helper.remove = function(){} //將json轉爲字符串,事件傳輸的參數類型轉化 helper.changeJSON2Query = function(){} //將相對路徑解析成文檔全路徑 helper.normalize = function(){}
採集邏輯
var collect = { deviceUrl:'http://collect.trc.com/rest/collect/device/h5/v1', eventUrl:'http://collect.trc.com/rest/collect/event/h5/v1', isuploadUrl:'http://collect.trc.com/rest/collect/isupload/app/v1', parmas:{ ExtraInfo:{} }, device:{} }; //獲取埋點配置 collect.setParames = function(){} //更新訪問路徑及頁面信息 collect.updatePageInfo = function(){} //獲取事件參數 collect.getParames = function(){} //獲取設備信息 collect.getDevice = function(){} //事件採集 collect.send = function(){} //設備採集 collect.sendDevice = function(){} //判斷才否採集,埋點採集的開關 collect.isupload = function(){ 1. 判斷是否採集,不採集就註銷事件監聽(項目中區分遊客身份和用戶身份的採集狀況,這個方法會被判斷兩次) 2. 採集則判斷是否已經採集過 a.已經採集過不作任何操做 b.沒有采集過添加事件監聽 3. 判斷是 混合應用仍是純 web 應用 a.若是是web 應用,調用 collect.setIframe 設置 iframe b.若是是混合應用 將開始加載和加載完成事件傳輸給 app } //點擊事件處理函數 collect.clickHandler = function(){} //離開頁面的事件處理函數 collect.beforeUnloadHandler = function(){} //頁面回退事件處理函數 collect.onPopStateHandler = function(){} //系統事件初始化,註冊離開事件,瀏覽器後退事件 collect.event = function(){} //獲取記錄開始加載數據信息 collect.getBeforeload = function(){} //存儲加載完成,獲取設備類型,記錄加載完成信息 collect.onload = function(){ 1. 判斷cookie是否有存設備類型信息,有表示混合應用 2. 採集加載完成時間等信息 3. 調用 collect.isupload 判斷是否進行採集 } //web 應用,經過嵌入 iframe 進行跨域 cookie 通信,設置設備id collect.setIframe = function(){} //app 與 h5 混合應用,直接將數信息發給 app,判斷設備類型作原生方法適配器 collect.saveEvent = function(){} //採集自定義事件類型 collect.dispatch = function(){} //將參數 userId 存入sessionStorage collect.storeUserId = function(){} //採集H5信息,若是是混合應用,將採集到的信息發送給 app 端 collect.saveEventInfo = function(){} //頁面初始化調用方法 collect.init = function(){ 1. 獲取開始加載的採集信息 2. 獲取 SDK 配置信息,設備信息 3. 改寫 history 兩個方法,單頁面應用頁面跳轉前調用咱們本身的方法 4. 頁面加載完成,調用 collect.onload 方法 } collect.init(); // 初始化 //暴露給業務方調用的方法 return { dispatch:collect.dispatch, storeUserId:collect.storeUserId, }
擴展
👆就是我這段時間研究的成果了,代碼的篇幅比較長,就不放在博客裏了,感興趣的同窗能夠加我微信進行交流,或則在文章下面留言,也歡迎你們給我提意見,幫忙優化 😝。
美團點評前端無痕埋點實踐 - 美團技術團隊
https://tech.meituan.com/2017/03/02/mt-mobile-analytics-practice.html
2017年03月02日 做者: 富強 朝旭 吳凱 文章連接 5287字 11分鐘閱讀
構建一個數據平臺,大致上包括數據採集、數據上報、數據存儲、數據計算以及數據可視化展現等幾個重要的環節。其中,數據採集與上報是整個流程中重要的一環,只有確保前端數據生產的全面、準確、及時,最終產生的數據結果纔是可靠的、有價值的。
爲了解決前端埋點的準確性、及時性、開發效率等問題,業內各家公司從不一樣角度,提出了多種技術方案,這些方案大致上能夠歸爲三類:
-
第一類是代碼埋點,即在須要埋點的節點調用接口直接上傳埋點數據,友盟、百度統計等第三方數據統計服務商大都採用這種方案;
-
第二類是可視化埋點,即經過可視化工具配置採集節點,在前端自動解析配置並上報埋點數據,從而實現所謂的「無痕埋點」, 表明方案是已經開源的Mixpanel;
-
第三類是「無埋點」,它並非真正的不須要埋點,而是前端自動採集所有事件並上報埋點數據,在後端數據計算時過濾出有用數據,表明方案是國內的GrowingIO。
美團點評對於前端埋點的要求很高,總結起來主要有三點需求:
-
數據的準確性和及時性,數據質量的好壞將直接影響依賴埋點數據的後端策略服務、與合做夥伴結算、以及運營數據報表等等。
-
埋點的效率,埋點的複雜度每每與業務需求相關,埋點效率會影響版本迭代的速度。
-
動態部署與修復埋點的能力,本質上這也是提高埋點效率的一種手段,而且使埋點再也不依賴於客戶端發版。
公司原有埋點主要採用手動代碼埋點的方案,代碼埋點雖然使用起來靈活,可是開發成本較高,而且一旦上線就很難修改。若是發生嚴重的數據問題,咱們只能經過發熱修復解決。若是直接改進爲可視化埋點,開發成本較高,而且也不能解決全部埋點需求;改進爲無埋點的話,帶來的流量消耗和數據計算成本也是業務不能接受的。所以,咱們在原有代碼埋點方案的基礎上,演化出了一套輕量的、聲明式的前端埋點方案,而且在動態埋點、無痕埋點等方向作了進一步的探索和實踐。
代碼埋點
因爲後面要介紹的聲明式埋點和無痕埋點方案仍然依賴原有代碼埋點的底層邏輯,這裏有必要簡單介紹下代碼埋點。在實現代碼埋點時,咱們主要關注的是數據結構的規範性、埋點接口的易用性、上報策略的可靠性等問題。總體的模塊劃分以下圖所示。
開發者須要手動在須要埋點的節點處(例如:點擊事件的回調方法、列表元素的展現回調方法、頁面的生命週期函數等等)插入這些埋點代碼。
EventInfo eventInfo = new EventInfo(); eventInfo.nm = EventName.MGE; // 事件類型爲MGE eventInfo.val_bid = "xxx"; // 事件的惟一標標識 eventInfo.val_lab = new HashMap<>(); // 攜帶的業務數據 eventInfo.val_lab.put(Constants.Business.xx,"xxx"); Statistics.getChannel("hotel").writeEvent(eventInfo);
能夠看出,代碼埋點是一種典型的命令式編程,所以埋點代碼經常要侵入具體的業務邏輯,這使埋點代碼變得很繁瑣而且容易出錯。所以,最直接的作法就是將埋點代碼與業務邏輯解耦,也就是「聲明式編程」,從而下降埋點的難度。
聲明式埋點
聲明式埋點的思路是將埋點代碼和具體的交互和業務邏輯解耦,開發者只用關心須要埋點的控件,而且爲這些控件聲明須要的埋點數據便可,從而下降埋點的成本。
Android
在Android中,咱們自定義了經常使用的UI控件,例如TextView、LinearLayout、ListView、ViewPager等,重寫了事件響應方法,在這些方法內部自動填寫埋點代碼。重寫控件的好處在於能夠攔截到更多的事件,執行效率高而且運行穩定。但其弊端也很是明顯——移植成本很高!
爲了解決這個問題,咱們借鑑了Android v7支持庫的思路,即經過AppCompatDelegate代理自動替換UI控件。
public class GAAppCompatDelegateV14 extends AppCompatDelegateImplV14 { @Override View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) { switch (name) { case "TextView": return new NovaTextView(context, attrs); } return super.callActivityOnCreateView(parent, name, context, attrs); } }
這樣,開發者只須要在本身的Activity基類中重寫getDelegate方法,將方法的返回值替換爲修改過的AppCompatDelegate,就能夠實現自動替換UI控件了。
@Override public AppCompatDelegate getDelegate() { if (mDelegate == null) { mDelegate = GAAppCompatUtil.create(this, this); } return mDelegate; }
然而,新的問題又出現了。
若是引用的第三方庫中重寫了UI控件,上述方法是不生效的,也就是說咱們須要一種替換UI控件類的父類方法。但是在運行時,咱們沒有找到可行的替換UI控件類的父類方法。所以,咱們嘗試在編譯時修改父類,並開發了一個Gradle插件。事實上,這樣作並不存在運行時效率的問題,只是會犧牲一些編譯速度。這樣開發者只須要運行這個插件,就能夠實現自動將UI控件的父類替換爲咱們重寫的UI控件了。
apply plugin: 'com.meituan.judasplugin'
採用了聲明式埋點後,只須要在控件初始化時聲明一下須要的埋點就能夠了。咱們沒必要再侵入程序的各類響應函數,下降了埋點的難度。
GAHelper.bindClick(view, bid, lab);
iOS
在iOS中,利用Objective-C關聯屬性和類別的語法特性,咱們無需重寫UI控件,就能實現聲明式打點。對於UIControl,能夠在聲明埋點時添加新的action,並在事件發生時自動填寫埋點代碼。
- (void)nvja_setAnalyticsParams:(NVJAMGEParameter *)params mgeType:(SAKStatisticsEventMGEType)type { if (self.wmja_clickParams == nil && type == SAKStatisticsEventClick) { [self addTarget:self action:@selector(wmja_controlDidTapped:) forControlEvents:UIControlEventTouchUpInside]; } [super nvja_setAnalyticsParams:params mgeType:type]; }
對於UITableView,能夠經過重寫UITableViewDelegate,利用消息傳遞機制攔截事件,並在事件回調方法中自動填寫埋點代碼。
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL selector = [anInvocation selector]; if (self.originalDelegate && [self.originalDelegate respondsToSelector:selector]) { [anInvocation invokeWithTarget:self.originalDelegate]; } SEL nvjaSelector = [self nvjaSelector:selector]; if ([super respondsToSelector:nvjaSelector]) { [anInvocation setSelector:nvjaSelector]; [anInvocation invokeWithTarget:self]; } }
一樣的,採用了聲明式埋點後,埋點代碼獲得了簡化。
NVJAMGEParameter *parameter = [[NVJAMGEParameter alloc] init]; parameter.bid = @"bid"; parameter.lab = @{@"poi_id":@"1"}; button.nvja_clickParams = parameter;
聲明式埋點可以替代全部的代碼埋點,而且能解決早期遇到的移植成本高等問題。可是其本質上仍是一種代碼埋點,只是埋點的代碼減小了,而且再也不侵入業務邏輯了。若是要知足動態部署與修復埋點的需求,就須要完全消滅寫死在前端的埋點代碼。
無痕埋點
咱們注意到,之因此聲明式埋點還須要寫死代碼,主要有兩個緣由:第一是須要聲明埋點控件的惟一事件標識,即bid;第二是有的業務字段須要在前端埋點時攜帶,而這些字段是在運行時纔可獲知的值。
對於第一點,咱們能夠嘗試在先後端使用一致的規則自動生成事件標識,這樣後端就能夠配置前端的埋點行爲,從而作到自動化埋點。對於第二點,能夠嘗試經過某種方式將業務數據自動與埋點數據關聯,這種關聯能夠發生在前端,也能夠發生在後端。
事件標識
爲了自動生成事件標識,咱們須要獲取每一個控件自身的ID、類名以及位於所屬父組件的Index等特徵信息,並逐級向上遍歷找到根節點。根節點通常是手動標記的,若是沒有標記則默認是視圖層次樹的頂層節點。最後,將遍歷產生的路徑上全部節點的特徵信息組合在一塊兒,就是這個事件的標識。考慮到在實際佈局中有可能存在一些動態插入的控件,咱們容許父組件的Index有必定的偏差。
配置後臺須要維護自動生成的事件標識和bid的映射關係,而且能夠下發給前端一個配置文件。當前端控件事件觸發時,自動和配置文件匹配就能夠拿到對應的bid了。須要注意的是,配置後臺維護事件標識的工做可不是一件輕鬆的事情,主要的複雜性在於不一樣版本之間佈局變動致使的事件標識變動,這就是爲何還須要手動標記根節點的緣由。因此,通常咱們會選取不易變動的視圖節點。
數據關聯
爲了實現業務數據與埋點數據的自動關聯,咱們起初嘗試了先後端日誌關聯的方式。即在前端請求後端API的時機,由後端將業務數據寫入日誌,最後在數據清洗時將相對應的先後端日誌合併。這種方式帶來的問題是後端改形成本較高,而且數據清洗的開銷較大,所以並不能普遍應用。可是在一些特殊場景下,例如某些業務數據只有後端能夠獲知,而前端不能獲知時,這種關聯是必要的。
更常見的數據關聯發生在前端數據之間。當頁面跳轉時,經過傳遞規範的跳轉URI Scheme,將業務數據傳遞給下個頁面,而且自動填入這個頁面的PV事件中。而該頁面內產生的全部其餘事件,都會攜帶與PV事件相同的業務數據。
這樣,經過自動產生事件標識並進行數據關聯,咱們就可以實現「無痕埋點」了,而且埋點節點能夠經過配置文件動態下發,從而具有了動態部署與修復埋點的能力。但須要注意的是,這種「無痕埋點」並不能解決全部問題,當業務字段沒法經過數據關聯獲取時(這種狀況比較常見),仍然須要開發者代碼埋點或聲明式埋點指定業務字段。就目前實踐階段的數據來看,業務中大約70%左右的埋點需求能夠經過無痕埋點解決,而對於另外30%的埋點需求,仍然須要使用聲明式埋點和代碼埋點。
總結
前端數據採集與上報是構建數據平臺過程當中最重要的環節,美團點評前端天天上報的數據達到百億次級別。爲了更好的知足公司各業務日益複雜的埋點需求,以及對埋點準確性、及時性、開發效率的要求,咱們在代碼埋點方案的基礎上演化出了一套輕量的、聲明式的前端埋點方案,而且在動態埋點、無痕埋點等方向作了進一步的探索和實踐。目前聲明式埋點已經在部分業務上全量使用,從數據質量和開發者反饋來看,取得了預期的收益。而無痕埋點也正在一些業務上驗證和持續優化中,後面也會在公司範圍內進一步推廣。
在實踐中咱們認識到,埋點問題不能經過單一一種技術方案來解決,在不一樣場景下咱們須要選擇不一樣的埋點方案。例如對於簡單的用戶行爲類事件,可使用無痕埋點解決;而對於須要攜帶大量運行時纔可獲知的業務字段的埋點需求,就須要聲明式埋點來解決。從更高的層面來看,除了前端埋點技術的優化,埋點數據的規範化、先後端協同埋點、數據清洗和關聯對於將來構建更加自動化、動態化的埋點體系一樣很是重要。