1. 程式人生 > 其它 >網站資料統計分析之二:前端日誌採集是與非

網站資料統計分析之二:前端日誌採集是與非

在上一篇《網站資料統計分析之一:日誌收集原理及其實現》中,咱們詳細的介紹了整個日誌採集的原理與流程。但是不是這樣在真實的業務環境中就萬事大吉了呢?事實往往並非如此。比如針對前端採集日誌,業務的同學經常會有疑問:你們的資料怎麼和後端日誌對不上呢?後端比你們多了 N%!技術的同學也會問:你們怎麼不打後端記日誌呢?後端比你們效率和準確性更高。帶著這些疑問今天咱們就來聊聊前端日誌採集中的這些是是非非。

1、前端 VS 後端到底哪個準?該用誰?

這應該算是統計分析同學最為關注的問題之一了,到底哪個準我們應該從技術和業務兩個角度來看待這個問題。

1.1 從技術架構層面日誌分類

日誌採集從技術架構層面而言就兩種,前端與後端。前端日誌採集說白了也就是頁面部署統計程式碼,通過

 <img src='/log_xxx.gif?k=v'> 或者 javascript 傳送 ajax 請求的方式來發送日誌請求。後端一般在 webCGI 中通過日誌 API 介面輸出日誌(比如 java 中 log4j),或者直接 webServer 中列印日誌(比如 Tomcat)。那這兩種技術方案各有何優劣呢?

1.1.1 前端 JS 採集

優勢:輕量,除錯友好,可擴充套件性維護性好

劣勢:資料不安全,易丟失,客戶端環境複雜相容成本高

1.1.2 後端服務採集

優勢:資料完整性有保證,業務資料安全

劣勢:對後端業務程式碼有一定侵入性,容易受爬蟲影響,非後臺互動行為日誌採集不到

通過上面比較我們可以看到前後端採集方案各有優劣,僅從資料量角度而言,後端日誌採集方案能保證日誌更為完整準確。

1.2 從業務架構層面日誌分類

從業務架構出發,日誌主要分為三大類:

  • 行為日誌:瀏覽、點選、各種互動行為等

行為日誌一般側重於使用者各種行為互動、使用者屬性採集,用來評估使用者體驗、運營效果或者最後資料探勘。比如漏斗、留存分析。

  • 業務日誌:使用者、帖子、訂單、庫存等

業務日誌往往和後端資料庫、應用服務強關聯,並且往往對日誌有特別高的安全、效能、穩定、準確性要求,比如計費、支付等。

  • 系統日誌

這類日誌一般用來衡量監控系統健康狀況,比如磁碟、頻寬是否滿了,機器負載是否很高,或者RD自己通過程式輸出的應用日誌,用來監控應用服務是否異常,比如介面是否有超時,是否有惡意訪問等。

1.3 前後端差異的原因

1.3.1 記錄日誌時機不同

對於行為日誌而言,前端 js 採集指令碼為了不影響主體業務邏輯以及取得相應業務引數,一般放在頁面底部。假設咱們某個頁面200個請求,後端日誌會在某個請求返回給客戶端之前就記錄日誌,而前端日誌此時就比較吃虧了,需要等到瀏覽器執行完200個請求到頁面底部 js 時,才能發出請求,這當中的時間差是日誌差距的主要原因之一。那麼問題就來了,如果一個頁面使用者開啟後沒載入執行完(因為前置js錯誤、效能延時、主動關閉等),應該算一個 pv/uv 嗎?這種場景下,一般是認為不應該算的,很顯然後端“搶跑”了,而且會比前端多不少。這個差異和你的頁面複雜程度,使用者網路質量密切相關,就實測資料來看,頁面頂部到頁面底部會有 10%左右差異,前端與後端會有20%以上差異。

1.3.2 爬蟲影響

這個和公司的業務密切相關,一般都會有競品或者其它商業、科研目的的爬蟲抓取網站資訊,低階的爬蟲不會觸發 js 請求,但會記錄伺服器日誌,高階的爬蟲封裝了瀏覽器核心的才會執行 js 程式碼,這也是前後端日誌差異的重要原因之一。

1.3.3 網路質量的原因

在移動端前端 js 請求丟失率更高,因為網路狀況非常複雜,2G、3G、4G、WiFi 等等,請求從客戶端發出來,由於不穩定的網路條件,不一定能到前端JS日誌伺服器。

1.3.4 平臺差異

M端部分瀏覽器預設是單標籤的瀏覽方式,任何一個點選、上一頁、下一頁按鈕都會導致下一個頁面會覆蓋上一個頁面,進而導致後端有日誌但是前端無法記錄到使用者日誌的情況。

另一種情況是可能部分老的移動端瀏覽器甚至都不支援 js,這就完全丟失了這部分日誌。

1.3.5 其它差異

快取、以及其它的使用者行為也可能導致請求執行到了,但是沒有傳送成功,比如使用者在頁面載入完成後,請求還未傳送完成時關掉頁面,可能導致請求被 cancel 掉,這對一些使用者黏性不是很強,跳出率很高的網站而言是另一個差異來源。

1.4 究竟該用誰? 

綜上所述,到底哪個準其實是相對而言的,得分業務場景來看,不可能有一個絕對值,至於用哪個就看你具體的業務訴求了。

  • 前端 JS 日誌只適合用來做全流量分析與統計,更多的是用來反應整體的流量趨勢與使用者行為,並不能精確到單個的使用者行為與單次的訪問軌跡。它的優勢在於與後端解耦,除錯、擴充套件、採集方便,額外的開發成本很低,適合做成 SAAS 模式。這也是為什麼百度統計、GA它們採用這套方案能做成一款行業通用,甚至全球化的資料產品。
  • 如果對日誌有特別高要求的業務場景比如計費、支付等等,要求日誌一條不丟同時日誌安全穩定,那就必須依賴資料庫或者後端日誌,但相應的開發維護成本會大些。

2、GA、百度統計、自己的日誌,到底哪個準?

記得幾年前流行過一句經典語錄:幸福是個比較級,要有東西墊底才感覺的到。言外之意也就是說凡事經過比較後,才能區分出優劣。回到咱們的話題,早期創業公司一般會選擇第三方統計系統,一來成本低,二來投資人往往需要看第三方資料對你公司的業務運營狀態作出評估或者估值。但第三方統計只能做通用統計,對個性化的統計與深度資料探勘無能為力,而且企業的商業機密堪憂。因此業務做大後公司往往會選擇自己搭建資料平臺和日誌採集系統。那麼問題馬上接踵而至:自己的日誌怎麼和 GA、百度統計對不上呢?甚至這幾者中任意兩個都存在一定差距。

其實原因大抵都是1.3中提到的原因,除此之外還有比較細節的技術實現差異,比如請求大小,域名是否被遮蔽(比如去年 5月開始 GA 就被牆了)、第三方 Cookie、埋碼是否完全、統計口徑與規則等等。

當你發現其中存在差異時,需要驗證各種可能原因去校驗資料,如無特殊原因,最終應該以自己採集的為準。

3、資料丟了嗎?丟多少?

在 1.3.1 中提到了一些內部線上測試資料,外界也有一些同學做過類似的測試,結論都差不多,部分業務場景下丟失率高到不可思議。比如點選前傳送日誌然後立即跳轉,如果不做任何優化處理,這種場景的丟失率巨高,往往超過 50%。

4、前端日誌採集丟失問題能解決嗎?

4.1 傳統解決方案

從技術角度可以歸納為兩點:

  • 使用者關閉頁面過早,統計指令碼還未載入/初始化完成
  • 使用者關閉或者跳出頁面的時候,請求未發出

針對第一點,概率較小,一般的處理方式就是,不要把統計指令碼參合到其他指令碼中,單獨載入,並且放在前頭,讓它優先載入。很多公司的做法是,不讓開發者關心統計指令碼的載入,使用者請求頁面的時候,Nginx 會在 Body 開始標籤位置注入一段指令碼。或者把 js 動態請求換成 <img src> 硬編碼的方式傳送請求。

對於問題二,處理方案就有很多了。

4.1.1 阻塞式的 Ajax 請求

還記得 XMLHttpRequest::open 方法的第三個引數吧,如果設定為 false 就是同步載入,

window.addEventListener('unload', function(event) {
  var xhr = new XMLHttpRequest(),
  xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
  xhr.open('post', '/log', false); // 同步請求
  xhr.send(data);
});

阻塞頁面關閉,當然可以在 readState 為 2 的時候就 abort 請求,因為我們不關心響應的內容,只要請求發出去就行了。

4.1.2 暴力的死迴圈

原理跟上面類似,只不過是使用一個空的死迴圈阻塞頁面關閉,

window.addEventListener('unload', function(event) {
  send(data);
  var now = +new Date;
  while(new Date - now >= 10) {} // 阻塞 10ms
});

4.1.3 發一個圖片請求阻塞

大部分瀏覽器都會等待圖片的載入,趁這個機會把統計資料傳送出去

window.addEventListener('unload', function(event) {
  send(data);
  (new Image).src = 'http://example.com/s.gif';
});

以上提到的幾個方案都是一個原理,讓瀏覽器繼續保持阻塞狀態,等資料傳送出去後再跳轉,這裡存在的問題是:

  • 少量瀏覽器下可能不奏效
  • 等待一會兒再跳轉,使用者體驗上打了折扣,尤其是移動端上

是否有更好的方案解決這個問題呢,前端同學秉著「小強精神」也提出了兩個可實踐的方案。

4.2 優化方案

不就是埋點統計資料嘛,非得在當前頁面傳送出去?優化方案的思路具有一定的跳躍性,我們考慮將資料在下跳頁中傳送,那麼問題就轉換為,如何將資料傳遞給下跳頁?

對於連結點選量的統計,我們可以將連結資訊通過 url 傳遞給下跳頁,傳遞思路如下:

4.2.1 url 傳參

通過陣列標識一個連結的位置資訊,如 [站點id,頁面id,模組id,連結index],通過四個引數可以惟一標識連結位置屬性,使用 URL param 引數將陣列資料傳遞給下跳頁,等待由下跳頁將資料傳送出去。

這裡存在的問題是,下跳頁中必須部署同樣的統計指令碼,但對一個系統來說,這是很容易做到的。我們也不會在自己的網頁上放其他網站的連結吧,所以整個資料的統計都在一個閉環內。還有就是如果統計業務複雜的情況下,這種方案的維護成本與使用者體驗很差。

4.2.2 通過 window.name 傳遞資料

window.name 是瀏覽器給我們開放的一個介面,設定該屬性的值後,即便頁面發生了跳轉,這個值依然不會變化,並且可以跨域使用。

這裡存在的問題是,該屬性可能被開發者用於其他途徑。我們可以限制開發者直接使用 window.name,封裝介面,通過介面呼叫,如 aralejs 提供的 nameStorage

nameStorage.setItem(key, value);
nameStorage.getItem(key);
nameStorage.removeItem(key);

儲存形式為:

    scheme                  nameStorage datas
      |                            |
------------           ------------------------
nameStorage:origin-name?key1=value1&key2=value2
            -----------
                 |
         window origin name

以上雖然基本解決了資料丟失和體驗差的問題,但是這也很大程度依賴於開發者的程式設計習慣,如不能隨便玩耍 window.name ,業務複雜的場景下容易出問題,比如容易被覆蓋;也對系統有一定的要求,必須在所有頁面上部署同樣的埋點指令碼。

4.2.3 localstorage 儲存重發

localstorage 是 HTML5 提供的兩種在客戶端儲存資料的新方法之一,對於丟失率高的場景,咱們可以先把請求日誌儲存在 localstorage 中,失敗後在下個頁面重發,並且可以新增重試機制,這樣日誌的完整效能很大程度上提高。從效能角度講還可以統一發送,減少連線。

但是針對跳出率高的場景,這種方式實測效果並不明顯。

4.3 這件事情應該交給瀏覽器來解決

上面提到的各種方案,不乏黑科技,然而存在的問題還是一大堆,如果團隊的開發者執行力不夠,中途容易出現各種麻煩。所以真正能夠解決這個問題的,必然還是瀏覽器本身!

為什麼不能給使用者提供這樣一個 API,即使頁面跳轉了,也能夠將上個頁面的請求發出去呢?慶幸的是,W3C 工作組也想到了這個問題,提出了 Beacon API 的 草案

Beacon API 允許開發者傳送少量錯誤分析和上報的資訊,它的特點很明顯:

  • 在空閒的時候非同步傳送統計,不影響頁面諸如 JS、CSS Animation 等執行
  • 即使頁面在 unload 狀態下,也會非同步傳送統計,不影響頁面過渡/跳轉到下跳頁
  • 能夠被客戶端優化傳送,尤其在 Mobile 環境下,可以將 Beacon 請求合併到其他請求上,一同處理

sendBeacon 函式掛在在 navigator 上,在 unload 之前,這個函式一定是被初始化了的。其使用方式為:

window.addEventListener('unload', function(event) {
  navigator.sendBeacon('/collector', data);
});

navigator.sendBeacon(url, data);,第一個引數為資料上報的地址,第二個引數為要傳送的資料,支援的資料格式有:ArrayBufferView, Blob, DOMString, 和 FormData。

Beacon 的還有一個非常實用的移動端使用場景,當用戶從瀏覽器切換到其他 app 介面或者 Home 屏的時候,部分瀏覽器預設會停止頁面指令碼的執行,如果在這個時候使用了 unload 事件,可能會讓你失望,因為 unload 事件並不會觸發,此時,Beacon 就派上用途了,它是不會受影響的。

本節是對頁面打點丟失問題的簡單探討,枚舉了我們通常會用到的一些解決方案,可能不是很完善,如果你有更好的建議,可以提出來。

很多問題,我們絞盡腦汁,可能很少會考慮,這個問題是不是應該由我們來解決,或者說這個問題交給誰處理是最恰當的。本文的探討可以看到,瀏覽器本身才是最好的問題解決方,當網站流量變大之後,上面提到的丟失問題就更加明顯,這也迫使瀏覽器本身做了改善,自然也在情理之中。

5、總結

至此,本文探討了前端日誌採集過程中的一些常見問題,解釋了資料分析以及RD同學一些常見困惑,並提供了一些相應的優化思路與方案。文中提到的各種問題還是以 PV 為主,其實還存在另一個常見的指標差異:UV,這個指標的差異原因更為複雜,改天有空再詳細分享下。總之日誌採集與統計分析沒有部分同學想象的那麼簡單,這裡面的坑其實很多,需要大家不斷的去探索,從技術和業務角度去不斷優化改進,前路漫漫。

說明:本文第四節結合了 Refer [1] 以及本文作者的實際經驗整理而成,在此致謝,感謝分享。

6、Refer:

[1] 頁面跳轉時,統計資料丟失問題探討

http://www.barretlee.com/blog/2016/02/20/navigator-beacon-api/

[2] 網站資料統計分析之一:日誌收集原理及其實現

http://my.oschina.net/leejun2005/blog/292709

[3] 站長統計、百度統計、騰訊統計、Google Analytics 哪一統計的資料相對準確些?

https://www.zhihu.com/question/19955915

[4] 網站分析——我們的資料準確嗎?

http://bit.ly/1RZnvWi

[5] 為什麼兩個監測工具報告中的資料不同

http://bit.ly/1QebUBe

[6] JavaScript API 呼叫說明

http://help.dplus.cnzz.com/?p=62#dplus.track

[7] 資料採集與埋點

http://www.sensorsdata.cn/blog/shu-ju-jie-ru-yu-mai-dian/

[8] Beforeunload打點丟失原因分析及解決方案:

http://blogread.cn/it/article/6804?f=wb

[9] beforeunload丟失率統計:

http://ued.taobao.org/blog/2012/11/beforeunload%E4%B8%A2%E5%A4%B1%E7%8E%87%E7%BB%9F%E8%AE%A1/

[10] 網際網路資料分析的底層應用架構

https://zhuanlan.zhihu.com/p/24018306

[11] 資料處理中的準確性問題

http://bit.ly/2vkImzb