久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

網(wǎng)站統(tǒng)計(jì)中的數(shù)據(jù)收集原理及實(shí)現(xiàn)

 HiLinz 2016-02-29
作者 張洋 | 發(fā)布于 2012-10-24

網(wǎng)站數(shù)據(jù)統(tǒng)計(jì)分析工具是網(wǎng)站站長(zhǎng)和運(yùn)營(yíng)人員經(jīng)常使用的一種工具,,比較常用的有谷歌分析百度統(tǒng)計(jì)騰訊分析等等,。所有這些統(tǒng)計(jì)分析工具的第一步都是網(wǎng)站訪問(wèn)數(shù)據(jù)的收集,。目前主流的數(shù)據(jù)收集方式基本都是基于javascript的。本文將簡(jiǎn)要分析這種數(shù)據(jù)收集的原理,,并一步一步實(shí)際搭建一個(gè)實(shí)際的數(shù)據(jù)收集系統(tǒng),。

數(shù)據(jù)收集原理分析

簡(jiǎn)單來(lái)說(shuō),網(wǎng)站統(tǒng)計(jì)分析工具需要收集到用戶瀏覽目標(biāo)網(wǎng)站的行為(如打開(kāi)某網(wǎng)頁(yè),、點(diǎn)擊某按鈕,、將商品加入購(gòu)物車(chē)等)及行為附加數(shù)據(jù)(如某下單行為產(chǎn)生的訂單金額等)。早期的網(wǎng)站統(tǒng)計(jì)往往只收集一種用戶行為:頁(yè)面的打開(kāi),。而后用戶在頁(yè)面中的行為均無(wú)法收集,。這種收集策略能滿足基本的流量分析、來(lái)源分析,、內(nèi)容分析及訪客屬性等常用分析視角,,但是,隨著ajax技術(shù)的廣泛使用及電子商務(wù)網(wǎng)站對(duì)于電子商務(wù)目標(biāo)的統(tǒng)計(jì)分析的需求越來(lái)越強(qiáng)烈,,這種傳統(tǒng)的收集策略已經(jīng)顯得力不能及,。

后來(lái),Google在其產(chǎn)品谷歌分析中創(chuàng)新性的引入了可定制的數(shù)據(jù)收集腳本,,用戶通過(guò)谷歌分析定義好的可擴(kuò)展接口,,只需編寫(xiě)少量的javascript代碼就可以實(shí)現(xiàn)自定義事件和自定義指標(biāo)的跟蹤和分析。目前百度統(tǒng)計(jì),、搜狗分析等產(chǎn)品均照搬了谷歌分析的模式,。

其實(shí)說(shuō)起來(lái)兩種數(shù)據(jù)收集模式的基本原理和流程是一致的,只是后一種通過(guò)javascript收集到了更多的信息,。下面看一下現(xiàn)在各種網(wǎng)站統(tǒng)計(jì)工具的數(shù)據(jù)收集基本原理,。

流程概覽

首先通過(guò)一幅圖總體看一下數(shù)據(jù)收集的基本流程。

圖1. 網(wǎng)站統(tǒng)計(jì)數(shù)據(jù)收集基本流程

首先,,用戶的行為會(huì)觸發(fā)瀏覽器對(duì)被統(tǒng)計(jì)頁(yè)面的一個(gè)http請(qǐng)求,,這里姑且先認(rèn)為行為就是打開(kāi)網(wǎng)頁(yè)。當(dāng)網(wǎng)頁(yè)被打開(kāi),,頁(yè)面中的埋點(diǎn)javascript片段會(huì)被執(zhí)行,,用過(guò)相關(guān)工具的朋友應(yīng)該知道,,一般網(wǎng)站統(tǒng)計(jì)工具都會(huì)要求用戶在網(wǎng)頁(yè)中加入一小段javascript代碼,這個(gè)代碼片段一般會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽,,并將src指向一個(gè)單獨(dú)的js文件,,此時(shí)這個(gè)單獨(dú)的js文件(圖1中綠色節(jié)點(diǎn))會(huì)被瀏覽器請(qǐng)求到并執(zhí)行,這個(gè)js往往就是真正的數(shù)據(jù)收集腳本,。數(shù)據(jù)收集完成后,,js會(huì)請(qǐng)求一個(gè)后端的數(shù)據(jù)收集腳本(圖1中的backend),這個(gè)腳本一般是一個(gè)偽裝成圖片的動(dòng)態(tài)腳本程序,,可能由php,、python或其它服務(wù)端語(yǔ)言編寫(xiě),js會(huì)將收集到的數(shù)據(jù)通過(guò)http參數(shù)的方式傳遞給后端腳本,,后端腳本解析參數(shù)并按固定格式記錄到訪問(wèn)日志,,同時(shí)可能會(huì)在http響應(yīng)中給客戶端種植一些用于追蹤的cookie。

上面是一個(gè)數(shù)據(jù)收集的大概流程,,下面以谷歌分析為例,,對(duì)每一個(gè)階段進(jìn)行一個(gè)相對(duì)詳細(xì)的分析。

埋點(diǎn)腳本執(zhí)行階段

若要使用谷歌分析(以下簡(jiǎn)稱GA),,需要在頁(yè)面中插入一段它提供的javascript片段,這個(gè)片段往往被稱為埋點(diǎn)代碼,。下面是我的博客中所放置的谷歌分析埋點(diǎn)代碼截圖:

圖2. 谷歌分析埋點(diǎn)代碼

其中_gaq是GA的的全局?jǐn)?shù)組,,用于放置各種配置,其中每一條配置的格式為:

  1. _gaq.push(['Action', 'param1', 'param2', ...]);

Action指定配置動(dòng)作,,后面是相關(guān)的參數(shù)列表,。GA給的默認(rèn)埋點(diǎn)代碼會(huì)給出兩條預(yù)置配置,_setAccount用于設(shè)置網(wǎng)站標(biāo)識(shí)ID,,這個(gè)標(biāo)識(shí)ID是在注冊(cè)GA時(shí)分配的,。_trackPageview告訴GA跟蹤一次頁(yè)面訪問(wèn)。更多配置請(qǐng)參考:https://developers.google.com/analytics/devguides/collection/gajs/,。實(shí)際上,這個(gè)_gaq是被當(dāng)做一個(gè)FIFO隊(duì)列來(lái)用的,配置代碼不必出現(xiàn)在埋點(diǎn)代碼之前,,具體請(qǐng)參考上述鏈接的說(shuō)明,。

就本文來(lái)說(shuō),_gaq的機(jī)制不是重點(diǎn),,重點(diǎn)是后面匿名函數(shù)的代碼,,這才是埋點(diǎn)代碼真正要做的。這段代碼的主要目的就是引入一個(gè)外部的js文件(ga.js),,方式是通過(guò)document.createElement方法創(chuàng)建一個(gè)script并根據(jù)協(xié)議(http或https)將src指向?qū)?yīng)的ga.js,,最后將這個(gè)element插入頁(yè)面的dom樹(shù)上,。

注意ga.async = true的意思是異步調(diào)用外部js文件,即不阻塞瀏覽器的解析,,待外部js下載完成后異步執(zhí)行,。這個(gè)屬性是HTML5新引入的。

數(shù)據(jù)收集腳本執(zhí)行階段

數(shù)據(jù)收集腳本(ga.js)被請(qǐng)求后會(huì)被執(zhí)行,,這個(gè)腳本一般要做如下幾件事:

1,、通過(guò)瀏覽器內(nèi)置javascript對(duì)象收集信息,如頁(yè)面title(通過(guò)document.title),、referrer(上一跳url,,通過(guò)document.referrer)、用戶顯示器分辨率(通過(guò)windows.screen),、cookie信息(通過(guò)document.cookie)等等一些信息,。

2、解析_gaq收集配置信息,。這里面可能會(huì)包括用戶自定義的事件跟蹤,、業(yè)務(wù)數(shù)據(jù)(如電子商務(wù)網(wǎng)站的商品編號(hào)等)等。

3,、將上面兩步收集的數(shù)據(jù)按預(yù)定義格式解析并拼接,。

4、請(qǐng)求一個(gè)后端腳本,,將信息放在http request參數(shù)中攜帶給后端腳本,。

這里唯一的問(wèn)題是步驟4,javascript請(qǐng)求后端腳本常用的方法是ajax,,但是ajax是不能跨域請(qǐng)求的,。這里ga.js在被統(tǒng)計(jì)網(wǎng)站的域內(nèi)執(zhí)行,而后端腳本在另外的域(GA的后端統(tǒng)計(jì)腳本是http://www./__utm.gif),,ajax行不通,。一種通用的方法是js腳本創(chuàng)建一個(gè)Image對(duì)象,將Image對(duì)象的src屬性指向后端腳本并攜帶參數(shù),,此時(shí)即實(shí)現(xiàn)了跨域請(qǐng)求后端,。這也是后端腳本為什么通常偽裝成gif文件的原因。通過(guò)http抓包可以看到ga.js對(duì)__utm.gif的請(qǐng)求:

圖3. 后端腳本請(qǐng)求的http包

可以看到ga.js在請(qǐng)求__utm.gif時(shí)帶了很多信息,,例如utmsr=1280×1024是屏幕分辨率,,utmac=UA-35712773-1是_gaq中解析出的我的GA標(biāo)識(shí)ID等等。

值得注意的是,,__utm.gif未必只會(huì)在埋點(diǎn)代碼執(zhí)行時(shí)被請(qǐng)求,,如果用_trackEvent配置了事件跟蹤,則在事件發(fā)生時(shí)也會(huì)請(qǐng)求這個(gè)腳本,。

由于ga.js經(jīng)過(guò)了壓縮和混淆,,可讀性很差,,我們就不分析了,具體后面實(shí)現(xiàn)階段我會(huì)實(shí)現(xiàn)一個(gè)功能類似的腳本,。

后端腳本執(zhí)行階段

GA的__utm.gif是一個(gè)偽裝成gif的腳本,。這種后端腳本一般要完成以下幾件事情:

1、解析http請(qǐng)求參數(shù)的到信息,。

2,、從服務(wù)器(WebServer)中獲取一些客戶端無(wú)法獲取的信息,如訪客ip等,。

3,、將信息按格式寫(xiě)入log。

5,、生成一副1×1的空gif圖片作為響應(yīng)內(nèi)容并將響應(yīng)頭的Content-type設(shè)為image/gif,。

5、在響應(yīng)頭中通過(guò)Set-cookie設(shè)置一些需要的cookie信息,。

之所以要設(shè)置cookie是因?yàn)槿绻櫸ㄒ辉L客,,通常做法是如果在請(qǐng)求時(shí)發(fā)現(xiàn)客戶端沒(méi)有指定的跟蹤cookie,則根據(jù)規(guī)則生成一個(gè)全局唯一的cookie并種植給用戶,,否則Set-cookie中放置獲取到的跟蹤cookie以保持同一用戶cookie不變(見(jiàn)圖4),。

圖4. 通過(guò)cookie跟蹤唯一用戶的原理

這種做法雖然不是完美的(例如用戶清掉cookie或更換瀏覽器會(huì)被認(rèn)為是兩個(gè)用戶),但是是目前被廣泛使用的手段,。注意,,如果沒(méi)有跨站跟蹤同一用戶的需求,可以通過(guò)js將cookie種植在被統(tǒng)計(jì)站點(diǎn)的域下(GA是這么做的),,如果要全網(wǎng)統(tǒng)一定位,則通過(guò)后端腳本種植在服務(wù)端域下(我們待會(huì)的實(shí)現(xiàn)會(huì)這么做),。

系統(tǒng)的設(shè)計(jì)實(shí)現(xiàn)

根據(jù)上述原理,,我自己搭建了一個(gè)訪問(wèn)日志收集系統(tǒng)??傮w來(lái)說(shuō),,搭建這個(gè)系統(tǒng)要做如下的事:

圖5. 訪問(wèn)數(shù)據(jù)收集系統(tǒng)工作分解

下面詳述每一步的實(shí)現(xiàn)。我將這個(gè)系統(tǒng)叫做MyAnalytics,。

確定收集的信息

為了簡(jiǎn)單起見(jiàn),,我不打算實(shí)現(xiàn)GA的完整數(shù)據(jù)收集模型,而是收集以下信息,。

名稱 途徑 備注
訪問(wèn)時(shí)間 web server Nginx $msec
IP web server Nginx $remote_addr
域名 javascript document.domain
URL javascript document.URL
頁(yè)面標(biāo)題 javascript document.title
分辨率 javascript window.screen.height & width
顏色深度 javascript window.screen.colorDepth
Referrer javascript document.referrer
瀏覽客戶端 web server Nginx $http_user_agent
客戶端語(yǔ)言 javascript navigator.language
訪客標(biāo)識(shí) cookie
網(wǎng)站標(biāo)識(shí) javascript 自定義對(duì)象

埋點(diǎn)代碼

埋點(diǎn)代碼我將借鑒GA的模式,,但是目前不會(huì)將配置對(duì)象作為一個(gè)FIFO隊(duì)列用。一個(gè)埋點(diǎn)代碼的模板如下:

這里我啟用了二級(jí)域名analytics.,,統(tǒng)計(jì)腳本的名稱為ma.js,。當(dāng)然這里有一點(diǎn)小問(wèn)題,,因?yàn)槲也](méi)有https的服務(wù)器,所以如果一個(gè)https站點(diǎn)部署了代碼會(huì)有問(wèn)題,,不過(guò)這里我們先忽略吧,。

前端統(tǒng)計(jì)腳本

我寫(xiě)了一個(gè)不是很完善但能完成基本工作的統(tǒng)計(jì)腳本ma.js:

  1. (function () {
  2. var params = {};
  3. //Document對(duì)象數(shù)據(jù)
  4. if(document) {
  5. params.domain = document.domain || '';
  6. params.url = document.URL || '';
  7. params.title = document.title || '';
  8. params.referrer = document.referrer || '';
  9. }
  10. //Window對(duì)象數(shù)據(jù)
  11. if(window && window.screen) {
  12. params.sh = window.screen.height || 0;
  13. params.sw = window.screen.width || 0;
  14. params.cd = window.screen.colorDepth || 0;
  15. }
  16. //navigator對(duì)象數(shù)據(jù)
  17. if(navigator) {
  18. params.lang = navigator.language || '';
  19. }
  20. //解析_maq配置
  21. if(_maq) {
  22. for(var i in _maq) {
  23. switch(_maq[i][0]) {
  24. case '_setAccount':
  25. params.account = _maq[i][1];
  26. break;
  27. default:
  28. break;
  29. }
  30. }
  31. }
  32. //拼接參數(shù)串
  33. var args = '';
  34. for(var i in params) {
  35. if(args != '') {
  36. args += '&';
  37. }
  38. args += i + '=' + encodeURIComponent(params[i]);
  39. }
  40.  
  41. //通過(guò)Image對(duì)象請(qǐng)求后端腳本
  42. var img = new Image(1, 1);
  43. img.src = 'http://analytics./1.gif?' + args;
  44. })();

整個(gè)腳本放在匿名函數(shù)里,確保不會(huì)污染全局環(huán)境,。功能在原理一節(jié)已經(jīng)說(shuō)明,,不再贅述。其中1.gif是后端腳本,。

日志格式

日志采用每行一條記錄的方式,,采用不可見(jiàn)字符^A(ascii碼0x01,Linux下可通過(guò)ctrl + v ctrl + a輸入,,下文均用“^A”表示不可見(jiàn)字符0x01),,具體格式如下:

時(shí)間^AIP^A域名^AURL^A頁(yè)面標(biāo)題^AReferrer^A分辨率高^A分辨率寬^A顏色深度^A語(yǔ)言^A客戶端信息^A用戶標(biāo)識(shí)^A網(wǎng)站標(biāo)識(shí)

后端腳本

為了簡(jiǎn)單和效率考慮,我打算直接使用nginx的access_log做日志收集,,不過(guò)有個(gè)問(wèn)題就是nginx配置本身的邏輯表達(dá)能力有限,,所以我選用了OpenResty做這個(gè)事情。OpenResty是一個(gè)基于Nginx擴(kuò)展出的高性能應(yīng)用開(kāi)發(fā)平臺(tái),,內(nèi)部集成了諸多有用的模塊,,其中的核心是通過(guò)ngx_lua模塊集成了Lua,從而在nginx配置文件中可以通過(guò)Lua來(lái)表述業(yè)務(wù),。關(guān)于這個(gè)平臺(tái)我這里不做過(guò)多介紹,,感興趣的同學(xué)可以參考其官方網(wǎng)站http:///,或者這里有其作者章亦春(agentzh)做的一個(gè)非常有愛(ài)的介紹OpenResty的slide:http:///misc/slides/ngx-openresty-ecosystem/,,關(guān)于ngx_lua可以參考:https://github.com/chaoslawful/lua-nginx-module,。

首先,需要在nginx的配置文件中定義日志格式:

  1. log_format tick "$msec^A$remote_addr^A$u_domain^A$u_url^A$u_title^A$u_referrer^A$u_sh^A$u_sw^A$u_cd^A$u_lang^A$http_user_agent^A$u_utrace^A$u_account";

注意這里以u(píng)_開(kāi)頭的是我們待會(huì)會(huì)自己定義的變量,,其它的是nginx內(nèi)置變量,。

然后是核心的兩個(gè)location:

  1. location /1.gif {
  2. #偽裝成gif文件
  3. default_type image/gif;
  4. #本身關(guān)閉access_log,通過(guò)subrequest記錄log
  5. access_log off;
  6.  
  7. access_by_lua "
  8. -- 用戶跟蹤cookie名為_(kāi)_utrace
  9. local uid = ngx.var.cookie___utrace
  10. if not uid then
  11. -- 如果沒(méi)有則生成一個(gè)跟蹤cookie,,算法為md5(時(shí)間戳+IP+客戶端信息)
  12. uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent)
  13. end
  14. ngx.header['Set-Cookie'] = {'__utrace=' .. uid .. '; path=/'}
  15. if ngx.var.arg_domain then
  16. -- 通過(guò)subrequest到/i-log記錄日志,,將參數(shù)和用戶跟蹤cookie帶過(guò)去
  17. ngx.location.capture('/i-log?' .. ngx.var.args .. '&utrace=' .. uid)
  18. end
  19. ";
  20.  
  21. #此請(qǐng)求不緩存
  22. add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT";
  23. add_header Pragma "no-cache";
  24. add_header Cache-Control "no-cache, max-age=0, must-revalidate";
  25.  
  26. #返回一個(gè)1×1的空gif圖片
  27. empty_gif;
  28. }
  29.  
  30. location /i-log {
  31. #內(nèi)部location,不允許外部直接訪問(wèn)
  32. internal;
  33.  
  34. #設(shè)置變量,,注意需要unescape
  35. set_unescape_uri $u_domain $arg_domain;
  36. set_unescape_uri $u_url $arg_url;
  37. set_unescape_uri $u_title $arg_title;
  38. set_unescape_uri $u_referrer $arg_referrer;
  39. set_unescape_uri $u_sh $arg_sh;
  40. set_unescape_uri $u_sw $arg_sw;
  41. set_unescape_uri $u_cd $arg_cd;
  42. set_unescape_uri $u_lang $arg_lang;
  43. set_unescape_uri $u_utrace $arg_utrace;
  44. set_unescape_uri $u_account $arg_account;
  45.  
  46. #打開(kāi)日志
  47. log_subrequest on;
  48. #記錄日志到ma.log,,實(shí)際應(yīng)用中最好加buffer,格式為tick
  49. access_log /path/to/logs/directory/ma.log tick;
  50.  
  51. #輸出空字符串
  52. echo '';
  53. }

要完全解釋這段腳本的每一個(gè)細(xì)節(jié)有點(diǎn)超出本文的范圍,,而且用到了諸多第三方ngxin模塊(全都包含在OpenResty中了),,重點(diǎn)的地方我都用注釋標(biāo)出來(lái)了,可以不用完全理解每一行的意義,只要大約知道這個(gè)配置完成了我們?cè)谠硪还?jié)提到的后端邏輯就可以了,。

日志輪轉(zhuǎn)

真正的日志收集系統(tǒng)訪問(wèn)日志會(huì)非常多,,時(shí)間一長(zhǎng)文件變得很大,而且日志放在一個(gè)文件不便于管理,。所以通常要按時(shí)間段將日志切分,,例如每天或每小時(shí)切分一個(gè)日志。我這里為了效果明顯,,每一小時(shí)切分一個(gè)日志,。我是通過(guò)crontab定時(shí)調(diào)用一個(gè)shell腳本實(shí)現(xiàn)的,shell腳本如下:

  1. _prefix="/path/to/nginx"
  2. time=`date +%Y%m%d%H`
  3.  
  4. mv ${_prefix}/logs/ma.log ${_prefix}/logs/ma/ma-${time}.log
  5. kill -USR1 `cat ${_prefix}/logs/nginx.pid`

這個(gè)腳本將ma.log移動(dòng)到指定文件夾并重命名為ma-{yyyymmddhh}.log,,然后向nginx發(fā)送USR1信號(hào)令其重新打開(kāi)日志文件,。

然后再/etc/crontab里加入一行:

  1. 59 * * * * root /path/to/directory/rotatelog.sh

在每個(gè)小時(shí)的59分啟動(dòng)這個(gè)腳本進(jìn)行日志輪轉(zhuǎn)操作。

測(cè)試

下面可以測(cè)試這個(gè)系統(tǒng)是否能正常運(yùn)行了,。我昨天就在我的博客中埋了相關(guān)的點(diǎn),,通過(guò)http抓包可以看到ma.js和1.gif已經(jīng)被正確請(qǐng)求:

圖6. http包分析ma.js和1.gif的請(qǐng)求

同時(shí)可以看一下1.gif的請(qǐng)求參數(shù):

圖7. 1.gif的請(qǐng)求參數(shù)

相關(guān)信息確實(shí)也放在了請(qǐng)求參數(shù)中。

然后我tail打開(kāi)日志文件,,然后刷新一下頁(yè)面,,因?yàn)闆](méi)有設(shè)access log buffer, 我立即得到了一條新日志:

  1. 1351060731.360^A0.0.0.0^Awww.^Ahttp://www./^ACodingLabs^A^A1024^A1280^A24^Azh-CN^AMozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4^A4d612be64366768d32e623d594e82678^AU-1-1

注意實(shí)際上原日志中的^A是不可見(jiàn)的,,這里我用可見(jiàn)的^A替換為方便閱讀,,另外IP由于涉及隱私我替換為了0.0.0.0。

看一眼日志輪轉(zhuǎn)目錄,,由于我之前已經(jīng)埋了點(diǎn),,所以已經(jīng)生成了很多輪轉(zhuǎn)文件:

圖8. 輪轉(zhuǎn)日志

關(guān)于分析

通過(guò)上面的分析和開(kāi)發(fā)可以大致理解一個(gè)網(wǎng)站統(tǒng)計(jì)的日志收集系統(tǒng)是如何工作的。有了這些日志,,就可以進(jìn)行后續(xù)的分析了,。本文只注重日志收集,所以不會(huì)寫(xiě)太多關(guān)于分析的東西,。

注意,,原始日志最好盡量多的保留信息而不要做過(guò)多過(guò)濾和處理。例如上面的MyAnalytics保留了毫秒級(jí)時(shí)間戳而不是格式化后的時(shí)間,,時(shí)間的格式化是后面的系統(tǒng)做的事而不是日志收集系統(tǒng)的責(zé)任,。后面的系統(tǒng)根據(jù)原始日志可以分析出很多東西,,例如通過(guò)IP庫(kù)可以定位訪問(wèn)者的地域,、user agent中可以得到訪問(wèn)者的操作系統(tǒng)、瀏覽器等信息,,再結(jié)合復(fù)雜的分析模型,,就可以做流量、來(lái)源、訪客,、地域,、路徑等分析了。當(dāng)然,,一般不會(huì)直接對(duì)原始日志分析,,而是會(huì)將其清洗格式化后轉(zhuǎn)存到其它地方,如MySQL或HBase中再做分析,。

分析部分的工作有很多開(kāi)源的基礎(chǔ)設(shè)施可以使用,,例如實(shí)時(shí)分析可以使用Storm,而離線分析可以使用Hadoop,。當(dāng)然,,在日志比較小的情況下,也可以通過(guò)shell命令做一些簡(jiǎn)單的分析,,例如,,下面三條命令可以分別得出我的博客在今天上午8點(diǎn)到9點(diǎn)的訪問(wèn)量(PV),訪客數(shù)(UV)和獨(dú)立IP數(shù)(IP):

  1. awk -F^A '{print $1}' ma-2012102409.log | wc -l
  2. awk -F^A '{print $12}' ma-2012102409.log | uniq | wc -l
  3. awk -F^A '{print $2}' ma-2012102409.log | uniq | wc -l

其它好玩的東西朋友們可以慢慢挖掘,。

參考

GA的開(kāi)發(fā)者文檔:https://developers.google.com/analytics/devguides/collection/gajs/

一篇關(guān)于實(shí)現(xiàn)nginx收日志的文章:http://blog./2011/11/?????¨nginxè?°??¥???

關(guān)于Nginx可以參考:http://wiki./Main

OpenResty的官方網(wǎng)站為:http://

ngx_lua模塊可參考:https://github.com/chaoslawful/lua-nginx-module

本文http抓包使用Chrome瀏覽器開(kāi)發(fā)者工具,,繪制思維導(dǎo)圖使用Xmind,流程和結(jié)構(gòu)圖使用Tikz PGF網(wǎng)站統(tǒng)計(jì)中的數(shù)據(jù)收集原理及實(shí)現(xiàn)

作者 張洋 | 發(fā)布于 2012-10-24

網(wǎng)站數(shù)據(jù)統(tǒng)計(jì)分析工具是網(wǎng)站站長(zhǎng)和運(yùn)營(yíng)人員經(jīng)常使用的一種工具,,比較常用的有谷歌分析,、百度統(tǒng)計(jì)騰訊分析等等。所有這些統(tǒng)計(jì)分析工具的第一步都是網(wǎng)站訪問(wèn)數(shù)據(jù)的收集,。目前主流的數(shù)據(jù)收集方式基本都是基于javascript的,。本文將簡(jiǎn)要分析這種數(shù)據(jù)收集的原理,并一步一步實(shí)際搭建一個(gè)實(shí)際的數(shù)據(jù)收集系統(tǒng),。

數(shù)據(jù)收集原理分析

簡(jiǎn)單來(lái)說(shuō),,網(wǎng)站統(tǒng)計(jì)分析工具需要收集到用戶瀏覽目標(biāo)網(wǎng)站的行為(如打開(kāi)某網(wǎng)頁(yè)、點(diǎn)擊某按鈕,、將商品加入購(gòu)物車(chē)等)及行為附加數(shù)據(jù)(如某下單行為產(chǎn)生的訂單金額等),。早期的網(wǎng)站統(tǒng)計(jì)往往只收集一種用戶行為:頁(yè)面的打開(kāi)。而后用戶在頁(yè)面中的行為均無(wú)法收集,。這種收集策略能滿足基本的流量分析,、來(lái)源分析、內(nèi)容分析及訪客屬性等常用分析視角,,但是,,隨著ajax技術(shù)的廣泛使用及電子商務(wù)網(wǎng)站對(duì)于電子商務(wù)目標(biāo)的統(tǒng)計(jì)分析的需求越來(lái)越強(qiáng)烈,這種傳統(tǒng)的收集策略已經(jīng)顯得力不能及,。

后來(lái),,Google在其產(chǎn)品谷歌分析中創(chuàng)新性的引入了可定制的數(shù)據(jù)收集腳本,,用戶通過(guò)谷歌分析定義好的可擴(kuò)展接口,只需編寫(xiě)少量的javascript代碼就可以實(shí)現(xiàn)自定義事件和自定義指標(biāo)的跟蹤和分析,。目前百度統(tǒng)計(jì),、搜狗分析等產(chǎn)品均照搬了谷歌分析的模式。

其實(shí)說(shuō)起來(lái)兩種數(shù)據(jù)收集模式的基本原理和流程是一致的,,只是后一種通過(guò)javascript收集到了更多的信息,。下面看一下現(xiàn)在各種網(wǎng)站統(tǒng)計(jì)工具的數(shù)據(jù)收集基本原理。

流程概覽

首先通過(guò)一幅圖總體看一下數(shù)據(jù)收集的基本流程,。

圖1. 網(wǎng)站統(tǒng)計(jì)數(shù)據(jù)收集基本流程

首先,,用戶的行為會(huì)觸發(fā)瀏覽器對(duì)被統(tǒng)計(jì)頁(yè)面的一個(gè)http請(qǐng)求,這里姑且先認(rèn)為行為就是打開(kāi)網(wǎng)頁(yè),。當(dāng)網(wǎng)頁(yè)被打開(kāi),,頁(yè)面中的埋點(diǎn)javascript片段會(huì)被執(zhí)行,用過(guò)相關(guān)工具的朋友應(yīng)該知道,,一般網(wǎng)站統(tǒng)計(jì)工具都會(huì)要求用戶在網(wǎng)頁(yè)中加入一小段javascript代碼,,這個(gè)代碼片段一般會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽,并將src指向一個(gè)單獨(dú)的js文件,,此時(shí)這個(gè)單獨(dú)的js文件(圖1中綠色節(jié)點(diǎn))會(huì)被瀏覽器請(qǐng)求到并執(zhí)行,,這個(gè)js往往就是真正的數(shù)據(jù)收集腳本。數(shù)據(jù)收集完成后,,js會(huì)請(qǐng)求一個(gè)后端的數(shù)據(jù)收集腳本(圖1中的backend),,這個(gè)腳本一般是一個(gè)偽裝成圖片的動(dòng)態(tài)腳本程序,可能由php,、python或其它服務(wù)端語(yǔ)言編寫(xiě),,js會(huì)將收集到的數(shù)據(jù)通過(guò)http參數(shù)的方式傳遞給后端腳本,后端腳本解析參數(shù)并按固定格式記錄到訪問(wèn)日志,,同時(shí)可能會(huì)在http響應(yīng)中給客戶端種植一些用于追蹤的cookie,。

上面是一個(gè)數(shù)據(jù)收集的大概流程,下面以谷歌分析為例,,對(duì)每一個(gè)階段進(jìn)行一個(gè)相對(duì)詳細(xì)的分析,。

埋點(diǎn)腳本執(zhí)行階段

若要使用谷歌分析(以下簡(jiǎn)稱GA),需要在頁(yè)面中插入一段它提供的javascript片段,,這個(gè)片段往往被稱為埋點(diǎn)代碼,。下面是我的博客中所放置的谷歌分析埋點(diǎn)代碼截圖:

圖2. 谷歌分析埋點(diǎn)代碼

其中_gaq是GA的的全局?jǐn)?shù)組,用于放置各種配置,,其中每一條配置的格式為:

  1. _gaq.push(['Action', 'param1', 'param2', ...]);

Action指定配置動(dòng)作,,后面是相關(guān)的參數(shù)列表。GA給的默認(rèn)埋點(diǎn)代碼會(huì)給出兩條預(yù)置配置,,_setAccount用于設(shè)置網(wǎng)站標(biāo)識(shí)ID,,這個(gè)標(biāo)識(shí)ID是在注冊(cè)GA時(shí)分配的。_trackPageview告訴GA跟蹤一次頁(yè)面訪問(wèn),。更多配置請(qǐng)參考:https://developers.google.com/analytics/devguides/collection/gajs/,。實(shí)際上,這個(gè)_gaq是被當(dāng)做一個(gè)FIFO隊(duì)列來(lái)用的,,配置代碼不必出現(xiàn)在埋點(diǎn)代碼之前,,具體請(qǐng)參考上述鏈接的說(shuō)明。

就本文來(lái)說(shuō),,_gaq的機(jī)制不是重點(diǎn),,重點(diǎn)是后面匿名函數(shù)的代碼,這才是埋點(diǎn)代碼真正要做的,。這段代碼的主要目的就是引入一個(gè)外部的js文件(ga.js),,方式是通過(guò)document.createElement方法創(chuàng)建一個(gè)script并根據(jù)協(xié)議(http或https)將src指向?qū)?yīng)的ga.js,最后將這個(gè)element插入頁(yè)面的dom樹(shù)上,。

注意ga.async = true的意思是異步調(diào)用外部js文件,,即不阻塞瀏覽器的解析,待外部js下載完成后異步執(zhí)行,。這個(gè)屬性是HTML5新引入的,。

數(shù)據(jù)收集腳本執(zhí)行階段

數(shù)據(jù)收集腳本(ga.js)被請(qǐng)求后會(huì)被執(zhí)行,這個(gè)腳本一般要做如下幾件事:

1,、通過(guò)瀏覽器內(nèi)置javascript對(duì)象收集信息,,如頁(yè)面title(通過(guò)document.title)、referrer(上一跳url,,通過(guò)document.referrer),、用戶顯示器分辨率(通過(guò)windows.screen)、cookie信息(通過(guò)document.cookie)等等一些信息,。

2,、解析_gaq收集配置信息。這里面可能會(huì)包括用戶自定義的事件跟蹤,、業(yè)務(wù)數(shù)據(jù)(如電子商務(wù)網(wǎng)站的商品編號(hào)等)等,。

3、將上面兩步收集的數(shù)據(jù)按預(yù)定義格式解析并拼接,。

4,、請(qǐng)求一個(gè)后端腳本,將信息放在http request參數(shù)中攜帶給后端腳本,。

這里唯一的問(wèn)題是步驟4,,javascript請(qǐng)求后端腳本常用的方法是ajax,但是ajax是不能跨域請(qǐng)求的,。這里ga.js在被統(tǒng)計(jì)網(wǎng)站的域內(nèi)執(zhí)行,,而后端腳本在另外的域(GA的后端統(tǒng)計(jì)腳本是http://www./__utm.gif),,ajax行不通。一種通用的方法是js腳本創(chuàng)建一個(gè)Image對(duì)象,,將Image對(duì)象的src屬性指向后端腳本并攜帶參數(shù),,此時(shí)即實(shí)現(xiàn)了跨域請(qǐng)求后端。這也是后端腳本為什么通常偽裝成gif文件的原因,。通過(guò)http抓包可以看到ga.js對(duì)__utm.gif的請(qǐng)求:

圖3. 后端腳本請(qǐng)求的http包

可以看到ga.js在請(qǐng)求__utm.gif時(shí)帶了很多信息,,例如utmsr=1280×1024是屏幕分辨率,utmac=UA-35712773-1是_gaq中解析出的我的GA標(biāo)識(shí)ID等等,。

值得注意的是,,__utm.gif未必只會(huì)在埋點(diǎn)代碼執(zhí)行時(shí)被請(qǐng)求,如果用_trackEvent配置了事件跟蹤,,則在事件發(fā)生時(shí)也會(huì)請(qǐng)求這個(gè)腳本,。

由于ga.js經(jīng)過(guò)了壓縮和混淆,可讀性很差,,我們就不分析了,,具體后面實(shí)現(xiàn)階段我會(huì)實(shí)現(xiàn)一個(gè)功能類似的腳本。

后端腳本執(zhí)行階段

GA的__utm.gif是一個(gè)偽裝成gif的腳本,。這種后端腳本一般要完成以下幾件事情:

1,、解析http請(qǐng)求參數(shù)的到信息。

2,、從服務(wù)器(WebServer)中獲取一些客戶端無(wú)法獲取的信息,,如訪客ip等。

3,、將信息按格式寫(xiě)入log,。

5、生成一副1×1的空gif圖片作為響應(yīng)內(nèi)容并將響應(yīng)頭的Content-type設(shè)為image/gif,。

5,、在響應(yīng)頭中通過(guò)Set-cookie設(shè)置一些需要的cookie信息。

之所以要設(shè)置cookie是因?yàn)槿绻櫸ㄒ辉L客,,通常做法是如果在請(qǐng)求時(shí)發(fā)現(xiàn)客戶端沒(méi)有指定的跟蹤cookie,,則根據(jù)規(guī)則生成一個(gè)全局唯一的cookie并種植給用戶,否則Set-cookie中放置獲取到的跟蹤cookie以保持同一用戶cookie不變(見(jiàn)圖4),。

圖4. 通過(guò)cookie跟蹤唯一用戶的原理

這種做法雖然不是完美的(例如用戶清掉cookie或更換瀏覽器會(huì)被認(rèn)為是兩個(gè)用戶),,但是是目前被廣泛使用的手段。注意,,如果沒(méi)有跨站跟蹤同一用戶的需求,,可以通過(guò)js將cookie種植在被統(tǒng)計(jì)站點(diǎn)的域下(GA是這么做的),如果要全網(wǎng)統(tǒng)一定位,,則通過(guò)后端腳本種植在服務(wù)端域下(我們待會(huì)的實(shí)現(xiàn)會(huì)這么做),。

系統(tǒng)的設(shè)計(jì)實(shí)現(xiàn)

根據(jù)上述原理,,我自己搭建了一個(gè)訪問(wèn)日志收集系統(tǒng)??傮w來(lái)說(shuō),,搭建這個(gè)系統(tǒng)要做如下的事:

圖5. 訪問(wèn)數(shù)據(jù)收集系統(tǒng)工作分解

下面詳述每一步的實(shí)現(xiàn)。我將這個(gè)系統(tǒng)叫做MyAnalytics,。

確定收集的信息

為了簡(jiǎn)單起見(jiàn),我不打算實(shí)現(xiàn)GA的完整數(shù)據(jù)收集模型,,而是收集以下信息,。

名稱 途徑 備注
訪問(wèn)時(shí)間 web server Nginx $msec
IP web server Nginx $remote_addr
域名 javascript document.domain
URL javascript document.URL
頁(yè)面標(biāo)題 javascript document.title
分辨率 javascript window.screen.height & width
顏色深度 javascript window.screen.colorDepth
Referrer javascript document.referrer
瀏覽客戶端 web server Nginx $http_user_agent
客戶端語(yǔ)言 javascript navigator.language
訪客標(biāo)識(shí) cookie
網(wǎng)站標(biāo)識(shí) javascript 自定義對(duì)象

埋點(diǎn)代碼

埋點(diǎn)代碼我將借鑒GA的模式,但是目前不會(huì)將配置對(duì)象作為一個(gè)FIFO隊(duì)列用,。一個(gè)埋點(diǎn)代碼的模板如下:

這里我啟用了二級(jí)域名analytics.,,統(tǒng)計(jì)腳本的名稱為ma.js。當(dāng)然這里有一點(diǎn)小問(wèn)題,,因?yàn)槲也](méi)有https的服務(wù)器,,所以如果一個(gè)https站點(diǎn)部署了代碼會(huì)有問(wèn)題,不過(guò)這里我們先忽略吧,。

前端統(tǒng)計(jì)腳本

我寫(xiě)了一個(gè)不是很完善但能完成基本工作的統(tǒng)計(jì)腳本ma.js:

  1. (function () {
  2. var params = {};
  3. //Document對(duì)象數(shù)據(jù)
  4. if(document) {
  5. params.domain = document.domain || '';
  6. params.url = document.URL || '';
  7. params.title = document.title || '';
  8. params.referrer = document.referrer || '';
  9. }
  10. //Window對(duì)象數(shù)據(jù)
  11. if(window && window.screen) {
  12. params.sh = window.screen.height || 0;
  13. params.sw = window.screen.width || 0;
  14. params.cd = window.screen.colorDepth || 0;
  15. }
  16. //navigator對(duì)象數(shù)據(jù)
  17. if(navigator) {
  18. params.lang = navigator.language || '';
  19. }
  20. //解析_maq配置
  21. if(_maq) {
  22. for(var i in _maq) {
  23. switch(_maq[i][0]) {
  24. case '_setAccount':
  25. params.account = _maq[i][1];
  26. break;
  27. default:
  28. break;
  29. }
  30. }
  31. }
  32. //拼接參數(shù)串
  33. var args = '';
  34. for(var i in params) {
  35. if(args != '') {
  36. args += '&';
  37. }
  38. args += i + '=' + encodeURIComponent(params[i]);
  39. }
  40.  
  41. //通過(guò)Image對(duì)象請(qǐng)求后端腳本
  42. var img = new Image(1, 1);
  43. img.src = 'http://analytics./1.gif?' + args;
  44. })();

整個(gè)腳本放在匿名函數(shù)里,,確保不會(huì)污染全局環(huán)境。功能在原理一節(jié)已經(jīng)說(shuō)明,,不再贅述,。其中1.gif是后端腳本。

日志格式

日志采用每行一條記錄的方式,,采用不可見(jiàn)字符^A(ascii碼0x01,,Linux下可通過(guò)ctrl + v ctrl + a輸入,下文均用“^A”表示不可見(jiàn)字符0x01),,具體格式如下:

時(shí)間^AIP^A域名^AURL^A頁(yè)面標(biāo)題^AReferrer^A分辨率高^A分辨率寬^A顏色深度^A語(yǔ)言^A客戶端信息^A用戶標(biāo)識(shí)^A網(wǎng)站標(biāo)識(shí)

后端腳本

為了簡(jiǎn)單和效率考慮,,我打算直接使用nginx的access_log做日志收集,不過(guò)有個(gè)問(wèn)題就是nginx配置本身的邏輯表達(dá)能力有限,,所以我選用了OpenResty做這個(gè)事情,。OpenResty是一個(gè)基于Nginx擴(kuò)展出的高性能應(yīng)用開(kāi)發(fā)平臺(tái),內(nèi)部集成了諸多有用的模塊,,其中的核心是通過(guò)ngx_lua模塊集成了Lua,,從而在nginx配置文件中可以通過(guò)Lua來(lái)表述業(yè)務(wù)。關(guān)于這個(gè)平臺(tái)我這里不做過(guò)多介紹,,感興趣的同學(xué)可以參考其官方網(wǎng)站http:///,,或者這里有其作者章亦春(agentzh)做的一個(gè)非常有愛(ài)的介紹OpenResty的slide:http:///misc/slides/ngx-openresty-ecosystem/,關(guān)于ngx_lua可以參考:https://github.com/chaoslawful/lua-nginx-module,。

首先,,需要在nginx的配置文件中定義日志格式:

  1. log_format tick "$msec^A$remote_addr^A$u_domain^A$u_url^A$u_title^A$u_referrer^A$u_sh^A$u_sw^A$u_cd^A$u_lang^A$http_user_agent^A$u_utrace^A$u_account";

注意這里以u(píng)_開(kāi)頭的是我們待會(huì)會(huì)自己定義的變量,,其它的是nginx內(nèi)置變量。

然后是核心的兩個(gè)location:

  1. location /1.gif {
  2. #偽裝成gif文件
  3. default_type image/gif;
  4. #本身關(guān)閉access_log,,通過(guò)subrequest記錄log
  5. access_log off;
  6.  
  7. access_by_lua "
  8. -- 用戶跟蹤cookie名為_(kāi)_utrace
  9. local uid = ngx.var.cookie___utrace
  10. if not uid then
  11. -- 如果沒(méi)有則生成一個(gè)跟蹤cookie,,算法為md5(時(shí)間戳+IP+客戶端信息)
  12. uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent)
  13. end
  14. ngx.header['Set-Cookie'] = {'__utrace=' .. uid .. '; path=/'}
  15. if ngx.var.arg_domain then
  16. -- 通過(guò)subrequest到/i-log記錄日志,將參數(shù)和用戶跟蹤cookie帶過(guò)去
  17. ngx.location.capture('/i-log?' .. ngx.var.args .. '&utrace=' .. uid)
  18. end
  19. ";
  20.  
  21. #此請(qǐng)求不緩存
  22. add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT";
  23. add_header Pragma "no-cache";
  24. add_header Cache-Control "no-cache, max-age=0, must-revalidate";
  25.  
  26. #返回一個(gè)1×1的空gif圖片
  27. empty_gif;
  28. }
  29.  
  30. location /i-log {
  31. #內(nèi)部location,,不允許外部直接訪問(wèn)
  32. internal;
  33.  
  34. #設(shè)置變量,,注意需要unescape
  35. set_unescape_uri $u_domain $arg_domain;
  36. set_unescape_uri $u_url $arg_url;
  37. set_unescape_uri $u_title $arg_title;
  38. set_unescape_uri $u_referrer $arg_referrer;
  39. set_unescape_uri $u_sh $arg_sh;
  40. set_unescape_uri $u_sw $arg_sw;
  41. set_unescape_uri $u_cd $arg_cd;
  42. set_unescape_uri $u_lang $arg_lang;
  43. set_unescape_uri $u_utrace $arg_utrace;
  44. set_unescape_uri $u_account $arg_account;
  45.  
  46. #打開(kāi)日志
  47. log_subrequest on;
  48. #記錄日志到ma.log,實(shí)際應(yīng)用中最好加buffer,,格式為tick
  49. access_log /path/to/logs/directory/ma.log tick;
  50.  
  51. #輸出空字符串
  52. echo '';
  53. }

要完全解釋這段腳本的每一個(gè)細(xì)節(jié)有點(diǎn)超出本文的范圍,,而且用到了諸多第三方ngxin模塊(全都包含在OpenResty中了),重點(diǎn)的地方我都用注釋標(biāo)出來(lái)了,,可以不用完全理解每一行的意義,,只要大約知道這個(gè)配置完成了我們?cè)谠硪还?jié)提到的后端邏輯就可以了。

日志輪轉(zhuǎn)

真正的日志收集系統(tǒng)訪問(wèn)日志會(huì)非常多,,時(shí)間一長(zhǎng)文件變得很大,,而且日志放在一個(gè)文件不便于管理。所以通常要按時(shí)間段將日志切分,,例如每天或每小時(shí)切分一個(gè)日志,。我這里為了效果明顯,每一小時(shí)切分一個(gè)日志,。我是通過(guò)crontab定時(shí)調(diào)用一個(gè)shell腳本實(shí)現(xiàn)的,,shell腳本如下:

  1. _prefix="/path/to/nginx"
  2. time=`date +%Y%m%d%H`
  3.  
  4. mv ${_prefix}/logs/ma.log ${_prefix}/logs/ma/ma-${time}.log
  5. kill -USR1 `cat ${_prefix}/logs/nginx.pid`

這個(gè)腳本將ma.log移動(dòng)到指定文件夾并重命名為ma-{yyyymmddhh}.log,然后向nginx發(fā)送USR1信號(hào)令其重新打開(kāi)日志文件,。

然后再/etc/crontab里加入一行:

  1. 59 * * * * root /path/to/directory/rotatelog.sh

在每個(gè)小時(shí)的59分啟動(dòng)這個(gè)腳本進(jìn)行日志輪轉(zhuǎn)操作,。

測(cè)試

下面可以測(cè)試這個(gè)系統(tǒng)是否能正常運(yùn)行了。我昨天就在我的博客中埋了相關(guān)的點(diǎn),,通過(guò)http抓包可以看到ma.js和1.gif已經(jīng)被正確請(qǐng)求:

圖6. http包分析ma.js和1.gif的請(qǐng)求

同時(shí)可以看一下1.gif的請(qǐng)求參數(shù):

圖7. 1.gif的請(qǐng)求參數(shù)

相關(guān)信息確實(shí)也放在了請(qǐng)求參數(shù)中,。

然后我tail打開(kāi)日志文件,然后刷新一下頁(yè)面,,因?yàn)闆](méi)有設(shè)access log buffer,, 我立即得到了一條新日志:

  1. 1351060731.360^A0.0.0.0^Awww.^Ahttp://www./^ACodingLabs^A^A1024^A1280^A24^Azh-CN^AMozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4^A4d612be64366768d32e623d594e82678^AU-1-1

注意實(shí)際上原日志中的^A是不可見(jiàn)的,這里我用可見(jiàn)的^A替換為方便閱讀,,另外IP由于涉及隱私我替換為了0.0.0.0,。

看一眼日志輪轉(zhuǎn)目錄,由于我之前已經(jīng)埋了點(diǎn),,所以已經(jīng)生成了很多輪轉(zhuǎn)文件:

圖8. 輪轉(zhuǎn)日志

關(guān)于分析

通過(guò)上面的分析和開(kāi)發(fā)可以大致理解一個(gè)網(wǎng)站統(tǒng)計(jì)的日志收集系統(tǒng)是如何工作的,。有了這些日志,就可以進(jìn)行后續(xù)的分析了。本文只注重日志收集,,所以不會(huì)寫(xiě)太多關(guān)于分析的東西,。

注意,原始日志最好盡量多的保留信息而不要做過(guò)多過(guò)濾和處理,。例如上面的MyAnalytics保留了毫秒級(jí)時(shí)間戳而不是格式化后的時(shí)間,,時(shí)間的格式化是后面的系統(tǒng)做的事而不是日志收集系統(tǒng)的責(zé)任。后面的系統(tǒng)根據(jù)原始日志可以分析出很多東西,,例如通過(guò)IP庫(kù)可以定位訪問(wèn)者的地域,、user agent中可以得到訪問(wèn)者的操作系統(tǒng)、瀏覽器等信息,,再結(jié)合復(fù)雜的分析模型,,就可以做流量、來(lái)源,、訪客,、地域,、路徑等分析了,。當(dāng)然,一般不會(huì)直接對(duì)原始日志分析,,而是會(huì)將其清洗格式化后轉(zhuǎn)存到其它地方,,如MySQL或HBase中再做分析。

分析部分的工作有很多開(kāi)源的基礎(chǔ)設(shè)施可以使用,,例如實(shí)時(shí)分析可以使用Storm,,而離線分析可以使用Hadoop。當(dāng)然,,在日志比較小的情況下,,也可以通過(guò)shell命令做一些簡(jiǎn)單的分析,例如,,下面三條命令可以分別得出我的博客在今天上午8點(diǎn)到9點(diǎn)的訪問(wèn)量(PV),,訪客數(shù)(UV)和獨(dú)立IP數(shù)(IP):

  1. awk -F^A '{print $1}' ma-2012102409.log | wc -l
  2. awk -F^A '{print $12}' ma-2012102409.log | uniq | wc -l
  3. awk -F^A '{print $2}' ma-2012102409.log | uniq | wc -l

其它好玩的東西朋友們可以慢慢挖掘。

參考

GA的開(kāi)發(fā)者文檔:https://developers.google.com/analytics/devguides/collection/gajs/

一篇關(guān)于實(shí)現(xiàn)nginx收日志的文章:http://blog./2011/11/?????¨nginxè?°??¥???

關(guān)于Nginx可以參考:http://wiki./Main

OpenResty的官方網(wǎng)站為:http://

ngx_lua模塊可參考:https://github.com/chaoslawful/lua-nginx-module

本文http抓包使用Chrome瀏覽器開(kāi)發(fā)者工具,,繪制思維導(dǎo)圖使用Xmind,,流程和結(jié)構(gòu)圖使用Tikz PGF網(wǎng)站統(tǒng)計(jì)中的數(shù)據(jù)收集原理及實(shí)現(xiàn)

作者 張洋 | 發(fā)布于 2012-10-24

網(wǎng)站數(shù)據(jù)統(tǒng)計(jì)分析工具是網(wǎng)站站長(zhǎng)和運(yùn)營(yíng)人員經(jīng)常使用的一種工具,比較常用的有谷歌分析,、百度統(tǒng)計(jì)騰訊分析等等,。所有這些統(tǒng)計(jì)分析工具的第一步都是網(wǎng)站訪問(wèn)數(shù)據(jù)的收集。目前主流的數(shù)據(jù)收集方式基本都是基于javascript的,。本文將簡(jiǎn)要分析這種數(shù)據(jù)收集的原理,,并一步一步實(shí)際搭建一個(gè)實(shí)際的數(shù)據(jù)收集系統(tǒng)。

數(shù)據(jù)收集原理分析

簡(jiǎn)單來(lái)說(shuō),,網(wǎng)站統(tǒng)計(jì)分析工具需要收集到用戶瀏覽目標(biāo)網(wǎng)站的行為(如打開(kāi)某網(wǎng)頁(yè),、點(diǎn)擊某按鈕,、將商品加入購(gòu)物車(chē)等)及行為附加數(shù)據(jù)(如某下單行為產(chǎn)生的訂單金額等)。早期的網(wǎng)站統(tǒng)計(jì)往往只收集一種用戶行為:頁(yè)面的打開(kāi),。而后用戶在頁(yè)面中的行為均無(wú)法收集,。這種收集策略能滿足基本的流量分析、來(lái)源分析,、內(nèi)容分析及訪客屬性等常用分析視角,,但是,隨著ajax技術(shù)的廣泛使用及電子商務(wù)網(wǎng)站對(duì)于電子商務(wù)目標(biāo)的統(tǒng)計(jì)分析的需求越來(lái)越強(qiáng)烈,,這種傳統(tǒng)的收集策略已經(jīng)顯得力不能及,。

后來(lái),Google在其產(chǎn)品谷歌分析中創(chuàng)新性的引入了可定制的數(shù)據(jù)收集腳本,,用戶通過(guò)谷歌分析定義好的可擴(kuò)展接口,,只需編寫(xiě)少量的javascript代碼就可以實(shí)現(xiàn)自定義事件和自定義指標(biāo)的跟蹤和分析。目前百度統(tǒng)計(jì),、搜狗分析等產(chǎn)品均照搬了谷歌分析的模式,。

其實(shí)說(shuō)起來(lái)兩種數(shù)據(jù)收集模式的基本原理和流程是一致的,只是后一種通過(guò)javascript收集到了更多的信息,。下面看一下現(xiàn)在各種網(wǎng)站統(tǒng)計(jì)工具的數(shù)據(jù)收集基本原理,。

流程概覽

首先通過(guò)一幅圖總體看一下數(shù)據(jù)收集的基本流程。

圖1. 網(wǎng)站統(tǒng)計(jì)數(shù)據(jù)收集基本流程

首先,,用戶的行為會(huì)觸發(fā)瀏覽器對(duì)被統(tǒng)計(jì)頁(yè)面的一個(gè)http請(qǐng)求,,這里姑且先認(rèn)為行為就是打開(kāi)網(wǎng)頁(yè)。當(dāng)網(wǎng)頁(yè)被打開(kāi),,頁(yè)面中的埋點(diǎn)javascript片段會(huì)被執(zhí)行,,用過(guò)相關(guān)工具的朋友應(yīng)該知道,一般網(wǎng)站統(tǒng)計(jì)工具都會(huì)要求用戶在網(wǎng)頁(yè)中加入一小段javascript代碼,,這個(gè)代碼片段一般會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽,,并將src指向一個(gè)單獨(dú)的js文件,此時(shí)這個(gè)單獨(dú)的js文件(圖1中綠色節(jié)點(diǎn))會(huì)被瀏覽器請(qǐng)求到并執(zhí)行,,這個(gè)js往往就是真正的數(shù)據(jù)收集腳本,。數(shù)據(jù)收集完成后,js會(huì)請(qǐng)求一個(gè)后端的數(shù)據(jù)收集腳本(圖1中的backend),,這個(gè)腳本一般是一個(gè)偽裝成圖片的動(dòng)態(tài)腳本程序,,可能由php、python或其它服務(wù)端語(yǔ)言編寫(xiě),,js會(huì)將收集到的數(shù)據(jù)通過(guò)http參數(shù)的方式傳遞給后端腳本,,后端腳本解析參數(shù)并按固定格式記錄到訪問(wèn)日志,同時(shí)可能會(huì)在http響應(yīng)中給客戶端種植一些用于追蹤的cookie。

上面是一個(gè)數(shù)據(jù)收集的大概流程,,下面以谷歌分析為例,,對(duì)每一個(gè)階段進(jìn)行一個(gè)相對(duì)詳細(xì)的分析。

埋點(diǎn)腳本執(zhí)行階段

若要使用谷歌分析(以下簡(jiǎn)稱GA),,需要在頁(yè)面中插入一段它提供的javascript片段,,這個(gè)片段往往被稱為埋點(diǎn)代碼。下面是我的博客中所放置的谷歌分析埋點(diǎn)代碼截圖:

圖2. 谷歌分析埋點(diǎn)代碼

其中_gaq是GA的的全局?jǐn)?shù)組,,用于放置各種配置,,其中每一條配置的格式為:

  1. _gaq.push(['Action', 'param1', 'param2', ...]);

Action指定配置動(dòng)作,后面是相關(guān)的參數(shù)列表,。GA給的默認(rèn)埋點(diǎn)代碼會(huì)給出兩條預(yù)置配置,,_setAccount用于設(shè)置網(wǎng)站標(biāo)識(shí)ID,這個(gè)標(biāo)識(shí)ID是在注冊(cè)GA時(shí)分配的,。_trackPageview告訴GA跟蹤一次頁(yè)面訪問(wèn),。更多配置請(qǐng)參考:https://developers.google.com/analytics/devguides/collection/gajs/。實(shí)際上,,這個(gè)_gaq是被當(dāng)做一個(gè)FIFO隊(duì)列來(lái)用的,,配置代碼不必出現(xiàn)在埋點(diǎn)代碼之前,具體請(qǐng)參考上述鏈接的說(shuō)明,。

就本文來(lái)說(shuō),,_gaq的機(jī)制不是重點(diǎn),,重點(diǎn)是后面匿名函數(shù)的代碼,,這才是埋點(diǎn)代碼真正要做的。這段代碼的主要目的就是引入一個(gè)外部的js文件(ga.js),,方式是通過(guò)document.createElement方法創(chuàng)建一個(gè)script并根據(jù)協(xié)議(http或https)將src指向?qū)?yīng)的ga.js,,最后將這個(gè)element插入頁(yè)面的dom樹(shù)上。

注意ga.async = true的意思是異步調(diào)用外部js文件,,即不阻塞瀏覽器的解析,,待外部js下載完成后異步執(zhí)行。這個(gè)屬性是HTML5新引入的,。

數(shù)據(jù)收集腳本執(zhí)行階段

數(shù)據(jù)收集腳本(ga.js)被請(qǐng)求后會(huì)被執(zhí)行,,這個(gè)腳本一般要做如下幾件事:

1、通過(guò)瀏覽器內(nèi)置javascript對(duì)象收集信息,,如頁(yè)面title(通過(guò)document.title),、referrer(上一跳url,通過(guò)document.referrer),、用戶顯示器分辨率(通過(guò)windows.screen),、cookie信息(通過(guò)document.cookie)等等一些信息。

2、解析_gaq收集配置信息,。這里面可能會(huì)包括用戶自定義的事件跟蹤,、業(yè)務(wù)數(shù)據(jù)(如電子商務(wù)網(wǎng)站的商品編號(hào)等)等。

3,、將上面兩步收集的數(shù)據(jù)按預(yù)定義格式解析并拼接,。

4、請(qǐng)求一個(gè)后端腳本,,將信息放在http request參數(shù)中攜帶給后端腳本,。

這里唯一的問(wèn)題是步驟4,javascript請(qǐng)求后端腳本常用的方法是ajax,,但是ajax是不能跨域請(qǐng)求的,。這里ga.js在被統(tǒng)計(jì)網(wǎng)站的域內(nèi)執(zhí)行,而后端腳本在另外的域(GA的后端統(tǒng)計(jì)腳本是http://www./__utm.gif),,ajax行不通,。一種通用的方法是js腳本創(chuàng)建一個(gè)Image對(duì)象,將Image對(duì)象的src屬性指向后端腳本并攜帶參數(shù),,此時(shí)即實(shí)現(xiàn)了跨域請(qǐng)求后端,。這也是后端腳本為什么通常偽裝成gif文件的原因。通過(guò)http抓包可以看到ga.js對(duì)__utm.gif的請(qǐng)求:

圖3. 后端腳本請(qǐng)求的http包

可以看到ga.js在請(qǐng)求__utm.gif時(shí)帶了很多信息,,例如utmsr=1280×1024是屏幕分辨率,,utmac=UA-35712773-1是_gaq中解析出的我的GA標(biāo)識(shí)ID等等。

值得注意的是,,__utm.gif未必只會(huì)在埋點(diǎn)代碼執(zhí)行時(shí)被請(qǐng)求,,如果用_trackEvent配置了事件跟蹤,則在事件發(fā)生時(shí)也會(huì)請(qǐng)求這個(gè)腳本,。

由于ga.js經(jīng)過(guò)了壓縮和混淆,,可讀性很差,我們就不分析了,,具體后面實(shí)現(xiàn)階段我會(huì)實(shí)現(xiàn)一個(gè)功能類似的腳本,。

后端腳本執(zhí)行階段

GA的__utm.gif是一個(gè)偽裝成gif的腳本。這種后端腳本一般要完成以下幾件事情:

1,、解析http請(qǐng)求參數(shù)的到信息,。

2、從服務(wù)器(WebServer)中獲取一些客戶端無(wú)法獲取的信息,,如訪客ip等,。

3、將信息按格式寫(xiě)入log,。

5,、生成一副1×1的空gif圖片作為響應(yīng)內(nèi)容并將響應(yīng)頭的Content-type設(shè)為image/gif,。

5、在響應(yīng)頭中通過(guò)Set-cookie設(shè)置一些需要的cookie信息,。

之所以要設(shè)置cookie是因?yàn)槿绻櫸ㄒ辉L客,,通常做法是如果在請(qǐng)求時(shí)發(fā)現(xiàn)客戶端沒(méi)有指定的跟蹤cookie,則根據(jù)規(guī)則生成一個(gè)全局唯一的cookie并種植給用戶,,否則Set-cookie中放置獲取到的跟蹤cookie以保持同一用戶cookie不變(見(jiàn)圖4),。

圖4. 通過(guò)cookie跟蹤唯一用戶的原理

這種做法雖然不是完美的(例如用戶清掉cookie或更換瀏覽器會(huì)被認(rèn)為是兩個(gè)用戶),但是是目前被廣泛使用的手段。注意,,如果沒(méi)有跨站跟蹤同一用戶的需求,,可以通過(guò)js將cookie種植在被統(tǒng)計(jì)站點(diǎn)的域下(GA是這么做的),,如果要全網(wǎng)統(tǒng)一定位,則通過(guò)后端腳本種植在服務(wù)端域下(我們待會(huì)的實(shí)現(xiàn)會(huì)這么做),。

系統(tǒng)的設(shè)計(jì)實(shí)現(xiàn)

根據(jù)上述原理,,我自己搭建了一個(gè)訪問(wèn)日志收集系統(tǒng),??傮w來(lái)說(shuō),搭建這個(gè)系統(tǒng)要做如下的事:

圖5. 訪問(wèn)數(shù)據(jù)收集系統(tǒng)工作分解

下面詳述每一步的實(shí)現(xiàn),。我將這個(gè)系統(tǒng)叫做MyAnalytics,。

確定收集的信息

為了簡(jiǎn)單起見(jiàn),我不打算實(shí)現(xiàn)GA的完整數(shù)據(jù)收集模型,,而是收集以下信息,。

名稱 途徑 備注
訪問(wèn)時(shí)間 web server Nginx $msec
IP web server Nginx $remote_addr
域名 javascript document.domain
URL javascript document.URL
頁(yè)面標(biāo)題 javascript document.title
分辨率 javascript window.screen.height & width
顏色深度 javascript window.screen.colorDepth
Referrer javascript document.referrer
瀏覽客戶端 web server Nginx $http_user_agent
客戶端語(yǔ)言 javascript navigator.language
訪客標(biāo)識(shí) cookie
網(wǎng)站標(biāo)識(shí) javascript 自定義對(duì)象

埋點(diǎn)代碼

埋點(diǎn)代碼我將借鑒GA的模式,,但是目前不會(huì)將配置對(duì)象作為一個(gè)FIFO隊(duì)列用,。一個(gè)埋點(diǎn)代碼的模板如下:

這里我啟用了二級(jí)域名analytics.,,統(tǒng)計(jì)腳本的名稱為ma.js,。當(dāng)然這里有一點(diǎn)小問(wèn)題,,因?yàn)槲也](méi)有https的服務(wù)器,,所以如果一個(gè)https站點(diǎn)部署了代碼會(huì)有問(wèn)題,,不過(guò)這里我們先忽略吧,。

前端統(tǒng)計(jì)腳本

我寫(xiě)了一個(gè)不是很完善但能完成基本工作的統(tǒng)計(jì)腳本ma.js:

  1. (function () {
  2. var params = {};
  3. //Document對(duì)象數(shù)據(jù)
  4. if(document) {
  5. params.domain = document.domain || '';
  6. params.url = document.URL || '';
  7. params.title = document.title || '';
  8. params.referrer = document.referrer || '';
  9. }
  10. //Window對(duì)象數(shù)據(jù)
  11. if(window && window.screen) {
  12. params.sh = window.screen.height || 0;
  13. params.sw = window.screen.width || 0;
  14. params.cd = window.screen.colorDepth || 0;
  15. }
  16. //navigator對(duì)象數(shù)據(jù)
  17. if(navigator) {
  18. params.lang = navigator.language || '';
  19. }
  20. //解析_maq配置
  21. if(_maq) {
  22. for(var i in _maq) {
  23. switch(_maq[i][0]) {
  24. case '_setAccount':
  25. params.account = _maq[i][1];
  26. break;
  27. default:
  28. break;
  29. }
  30. }
  31. }
  32. //拼接參數(shù)串
  33. var args = '';
  34. for(var i in params) {
  35. if(args != '') {
  36. args += '&';
  37. }
  38. args += i + '=' + encodeURIComponent(params[i]);
  39. }
  40.  
  41. //通過(guò)Image對(duì)象請(qǐng)求后端腳本
  42. var img = new Image(1, 1);
  43. img.src = 'http://analytics./1.gif?' + args;
  44. })();

整個(gè)腳本放在匿名函數(shù)里,,確保不會(huì)污染全局環(huán)境,。功能在原理一節(jié)已經(jīng)說(shuō)明,不再贅述,。其中1.gif是后端腳本,。

日志格式

日志采用每行一條記錄的方式,采用不可見(jiàn)字符^A(ascii碼0x01,,Linux下可通過(guò)ctrl + v ctrl + a輸入,下文均用“^A”表示不可見(jiàn)字符0x01),,具體格式如下:

時(shí)間^AIP^A域名^AURL^A頁(yè)面標(biāo)題^AReferrer^A分辨率高^A分辨率寬^A顏色深度^A語(yǔ)言^A客戶端信息^A用戶標(biāo)識(shí)^A網(wǎng)站標(biāo)識(shí)

后端腳本

為了簡(jiǎn)單和效率考慮,,我打算直接使用nginx的access_log做日志收集,不過(guò)有個(gè)問(wèn)題就是nginx配置本身的邏輯表達(dá)能力有限,,所以我選用了OpenResty做這個(gè)事情,。OpenResty是一個(gè)基于Nginx擴(kuò)展出的高性能應(yīng)用開(kāi)發(fā)平臺(tái),內(nèi)部集成了諸多有用的模塊,其中的核心是通過(guò)ngx_lua模塊集成了Lua,,從而在nginx配置文件中可以通過(guò)Lua來(lái)表述業(yè)務(wù),。關(guān)于這個(gè)平臺(tái)我這里不做過(guò)多介紹,,感興趣的同學(xué)可以參考其官方網(wǎng)站http:///,,或者這里有其作者章亦春(agentzh)做的一個(gè)非常有愛(ài)的介紹OpenResty的slide:http:///misc/slides/ngx-openresty-ecosystem/,關(guān)于ngx_lua可以參考:https://github.com/chaoslawful/lua-nginx-module,。

首先,,需要在nginx的配置文件中定義日志格式:

  1. log_format tick "$msec^A$remote_addr^A$u_domain^A$u_url^A$u_title^A$u_referrer^A$u_sh^A$u_sw^A$u_cd^A$u_lang^A$http_user_agent^A$u_utrace^A$u_account";

注意這里以u(píng)_開(kāi)頭的是我們待會(huì)會(huì)自己定義的變量,其它的是nginx內(nèi)置變量,。

然后是核心的兩個(gè)location:

  1. location /1.gif {
  2. #偽裝成gif文件
  3. default_type image/gif;
  4. #本身關(guān)閉access_log,,通過(guò)subrequest記錄log
  5. access_log off;
  6.  
  7. access_by_lua "
  8. -- 用戶跟蹤cookie名為_(kāi)_utrace
  9. local uid = ngx.var.cookie___utrace
  10. if not uid then
  11. -- 如果沒(méi)有則生成一個(gè)跟蹤cookie,算法為md5(時(shí)間戳+IP+客戶端信息)
  12. uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent)
  13. end
  14. ngx.header['Set-Cookie'] = {'__utrace=' .. uid .. '; path=/'}
  15. if ngx.var.arg_domain then
  16. -- 通過(guò)subrequest到/i-log記錄日志,,將參數(shù)和用戶跟蹤cookie帶過(guò)去
  17. ngx.location.capture('/i-log?' .. ngx.var.args .. '&utrace=' .. uid)
  18. end
  19. ";
  20.  
  21. #此請(qǐng)求不緩存
  22. add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT";
  23. add_header Pragma "no-cache";
  24. add_header Cache-Control "no-cache, max-age=0, must-revalidate";
  25.  
  26. #返回一個(gè)1×1的空gif圖片
  27. empty_gif;
  28. }
  29.  
  30. location /i-log {
  31. #內(nèi)部location,,不允許外部直接訪問(wèn)
  32. internal;
  33.  
  34. #設(shè)置變量,注意需要unescape
  35. set_unescape_uri $u_domain $arg_domain;
  36. set_unescape_uri $u_url $arg_url;
  37. set_unescape_uri $u_title $arg_title;
  38. set_unescape_uri $u_referrer $arg_referrer;
  39. set_unescape_uri $u_sh $arg_sh;
  40. set_unescape_uri $u_sw $arg_sw;
  41. set_unescape_uri $u_cd $arg_cd;
  42. set_unescape_uri $u_lang $arg_lang;
  43. set_unescape_uri $u_utrace $arg_utrace;
  44. set_unescape_uri $u_account $arg_account;
  45.  
  46. #打開(kāi)日志
  47. log_subrequest on;
  48. #記錄日志到ma.log,,實(shí)際應(yīng)用中最好加buffer,,格式為tick
  49. access_log /path/to/logs/directory/ma.log tick;
  50.  
  51. #輸出空字符串
  52. echo '';
  53. }

要完全解釋這段腳本的每一個(gè)細(xì)節(jié)有點(diǎn)超出本文的范圍,而且用到了諸多第三方ngxin模塊(全都包含在OpenResty中了),,重點(diǎn)的地方我都用注釋標(biāo)出來(lái)了,,可以不用完全理解每一行的意義,只要大約知道這個(gè)配置完成了我們?cè)谠硪还?jié)提到的后端邏輯就可以了,。

日志輪轉(zhuǎn)

真正的日志收集系統(tǒng)訪問(wèn)日志會(huì)非常多,,時(shí)間一長(zhǎng)文件變得很大,而且日志放在一個(gè)文件不便于管理,。所以通常要按時(shí)間段將日志切分,,例如每天或每小時(shí)切分一個(gè)日志。我這里為了效果明顯,,每一小時(shí)切分一個(gè)日志,。我是通過(guò)crontab定時(shí)調(diào)用一個(gè)shell腳本實(shí)現(xiàn)的,shell腳本如下:

  1. _prefix="/path/to/nginx"
  2. time=`date +%Y%m%d%H`
  3.  
  4. mv ${_prefix}/logs/ma.log ${_prefix}/logs/ma/ma-${time}.log
  5. kill -USR1 `cat ${_prefix}/logs/nginx.pid`

這個(gè)腳本將ma.log移動(dòng)到指定文件夾并重命名為ma-{yyyymmddhh}.log,,然后向nginx發(fā)送USR1信號(hào)令其重新打開(kāi)日志文件,。

然后再/etc/crontab里加入一行:

  1. 59 * * * * root /path/to/directory/rotatelog.sh

在每個(gè)小時(shí)的59分啟動(dòng)這個(gè)腳本進(jìn)行日志輪轉(zhuǎn)操作。

測(cè)試

下面可以測(cè)試這個(gè)系統(tǒng)是否能正常運(yùn)行了,。我昨天就在我的博客中埋了相關(guān)的點(diǎn),,通過(guò)http抓包可以看到ma.js和1.gif已經(jīng)被正確請(qǐng)求:

圖6. http包分析ma.js和1.gif的請(qǐng)求

同時(shí)可以看一下1.gif的請(qǐng)求參數(shù):

圖7. 1.gif的請(qǐng)求參數(shù)

相關(guān)信息確實(shí)也放在了請(qǐng)求參數(shù)中。

然后我tail打開(kāi)日志文件,,然后刷新一下頁(yè)面,,因?yàn)闆](méi)有設(shè)access log buffer, 我立即得到了一條新日志:

  1. 1351060731.360^A0.0.0.0^Awww.^Ahttp://www./^ACodingLabs^A^A1024^A1280^A24^Azh-CN^AMozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4^A4d612be64366768d32e623d594e82678^AU-1-1

注意實(shí)際上原日志中的^A是不可見(jiàn)的,這里我用可見(jiàn)的^A替換為方便閱讀,,另外IP由于涉及隱私我替換為了0.0.0.0,。

看一眼日志輪轉(zhuǎn)目錄,由于我之前已經(jīng)埋了點(diǎn),,所以已經(jīng)生成了很多輪轉(zhuǎn)文件:

圖8. 輪轉(zhuǎn)日志

關(guān)于分析

通過(guò)上面的分析和開(kāi)發(fā)可以大致理解一個(gè)網(wǎng)站統(tǒng)計(jì)的日志收集系統(tǒng)是如何工作的,。有了這些日志,就可以進(jìn)行后續(xù)的分析了,。本文只注重日志收集,,所以不會(huì)寫(xiě)太多關(guān)于分析的東西。

注意,,原始日志最好盡量多的保留信息而不要做過(guò)多過(guò)濾和處理,。例如上面的MyAnalytics保留了毫秒級(jí)時(shí)間戳而不是格式化后的時(shí)間,時(shí)間的格式化是后面的系統(tǒng)做的事而不是日志收集系統(tǒng)的責(zé)任,。后面的系統(tǒng)根據(jù)原始日志可以分析出很多東西,,例如通過(guò)IP庫(kù)可以定位訪問(wèn)者的地域、user agent中可以得到訪問(wèn)者的操作系統(tǒng),、瀏覽器等信息,,再結(jié)合復(fù)雜的分析模型,就可以做流量,、來(lái)源,、訪客、地域,、路徑等分析了,。當(dāng)然,一般不會(huì)直接對(duì)原始日志分析,,而是會(huì)將其清洗格式化后轉(zhuǎn)存到其它地方,,如MySQL或HBase中再做分析。

分析部分的工作有很多開(kāi)源的基礎(chǔ)設(shè)施可以使用,,例如實(shí)時(shí)分析可以使用Storm,,而離線分析可以使用Hadoop。當(dāng)然,,在日志比較小的情況下,,也可以通過(guò)shell命令做一些簡(jiǎn)單的分析,例如,,下面三條命令可以分別得出我的博客在今天上午8點(diǎn)到9點(diǎn)的訪問(wèn)量(PV),,訪客數(shù)(UV)和獨(dú)立IP數(shù)(IP):

  1. awk -F^A '{print $1}' ma-2012102409.log | wc -l
  2. awk -F^A '{print $12}' ma-2012102409.log | uniq | wc -l
  3. awk -F^A '{print $2}' ma-2012102409.log | uniq | wc -l

其它好玩的東西朋友們可以慢慢挖掘。

參考

GA的開(kāi)發(fā)者文檔:https://developers.google.com/analytics/devguides/collection/gajs/

一篇關(guān)于實(shí)現(xiàn)nginx收日志的文章:http://blog./2011/11/?????¨nginxè?°??¥???

關(guān)于Nginx可以參考:http://wiki./Main

OpenResty的官方網(wǎng)站為:http://

ngx_lua模塊可參考:https://github.com/chaoslawful/lua-nginx-module

本文http抓包使用Chrome瀏覽器開(kāi)發(fā)者工具,,繪制思維導(dǎo)圖使用Xmind,,流程和結(jié)構(gòu)圖使用Tikz PGF網(wǎng)站統(tǒng)計(jì)中的數(shù)據(jù)收集原理及實(shí)現(xiàn)

作者 張洋 | 發(fā)布于 2012-10-24

網(wǎng)站數(shù)據(jù)統(tǒng)計(jì)分析工具是網(wǎng)站站長(zhǎng)和運(yùn)營(yíng)人員經(jīng)常使用的一種工具,,比較常用的有谷歌分析百度統(tǒng)計(jì)騰訊分析等等,。所有這些統(tǒng)計(jì)分析工具的第一步都是網(wǎng)站訪問(wèn)數(shù)據(jù)的收集,。目前主流的數(shù)據(jù)收集方式基本都是基于javascript的。本文將簡(jiǎn)要分析這種數(shù)據(jù)收集的原理,,并一步一步實(shí)際搭建一個(gè)實(shí)際的數(shù)據(jù)收集系統(tǒng),。

數(shù)據(jù)收集原理分析

簡(jiǎn)單來(lái)說(shuō),網(wǎng)站統(tǒng)計(jì)分析工具需要收集到用戶瀏覽目標(biāo)網(wǎng)站的行為(如打開(kāi)某網(wǎng)頁(yè),、點(diǎn)擊某按鈕,、將商品加入購(gòu)物車(chē)等)及行為附加數(shù)據(jù)(如某下單行為產(chǎn)生的訂單金額等),。早期的網(wǎng)站統(tǒng)計(jì)往往只收集一種用戶行為:頁(yè)面的打開(kāi),。而后用戶在頁(yè)面中的行為均無(wú)法收集。這種收集策略能滿足基本的流量分析,、來(lái)源分析,、內(nèi)容分析及訪客屬性等常用分析視角,但是,,隨著ajax技術(shù)的廣泛使用及電子商務(wù)網(wǎng)站對(duì)于電子商務(wù)目標(biāo)的統(tǒng)計(jì)分析的需求越來(lái)越強(qiáng)烈,,這種傳統(tǒng)的收集策略已經(jīng)顯得力不能及。

后來(lái),,Google在其產(chǎn)品谷歌分析中創(chuàng)新性的引入了可定制的數(shù)據(jù)收集腳本,,用戶通過(guò)谷歌分析定義好的可擴(kuò)展接口,只需編寫(xiě)少量的javascript代碼就可以實(shí)現(xiàn)自定義事件和自定義指標(biāo)的跟蹤和分析,。目前百度統(tǒng)計(jì),、搜狗分析等產(chǎn)品均照搬了谷歌分析的模式。

其實(shí)說(shuō)起來(lái)兩種數(shù)據(jù)收集模式的基本原理和流程是一致的,,只是后一種通過(guò)javascript收集到了更多的信息,。下面看一下現(xiàn)在各種網(wǎng)站統(tǒng)計(jì)工具的數(shù)據(jù)收集基本原理。

流程概覽

首先通過(guò)一幅圖總體看一下數(shù)據(jù)收集的基本流程,。

圖1. 網(wǎng)站統(tǒng)計(jì)數(shù)據(jù)收集基本流程

首先,,用戶的行為會(huì)觸發(fā)瀏覽器對(duì)被統(tǒng)計(jì)頁(yè)面的一個(gè)http請(qǐng)求,這里姑且先認(rèn)為行為就是打開(kāi)網(wǎng)頁(yè),。當(dāng)網(wǎng)頁(yè)被打開(kāi),,頁(yè)面中的埋點(diǎn)javascript片段會(huì)被執(zhí)行,用過(guò)相關(guān)工具的朋友應(yīng)該知道,,一般網(wǎng)站統(tǒng)計(jì)工具都會(huì)要求用戶在網(wǎng)頁(yè)中加入一小段javascript代碼,,這個(gè)代碼片段一般會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽,并將src指向一個(gè)單獨(dú)的js文件,,此時(shí)這個(gè)單獨(dú)的js文件(圖1中綠色節(jié)點(diǎn))會(huì)被瀏覽器請(qǐng)求到并執(zhí)行,,這個(gè)js往往就是真正的數(shù)據(jù)收集腳本。數(shù)據(jù)收集完成后,js會(huì)請(qǐng)求一個(gè)后端的數(shù)據(jù)收集腳本(圖1中的backend),,這個(gè)腳本一般是一個(gè)偽裝成圖片的動(dòng)態(tài)腳本程序,,可能由php、python或其它服務(wù)端語(yǔ)言編寫(xiě),,js會(huì)將收集到的數(shù)據(jù)通過(guò)http參數(shù)的方式傳遞給后端腳本,,后端腳本解析參數(shù)并按固定格式記錄到訪問(wèn)日志,同時(shí)可能會(huì)在http響應(yīng)中給客戶端種植一些用于追蹤的cookie,。

上面是一個(gè)數(shù)據(jù)收集的大概流程,,下面以谷歌分析為例,對(duì)每一個(gè)階段進(jìn)行一個(gè)相對(duì)詳細(xì)的分析,。

埋點(diǎn)腳本執(zhí)行階段

若要使用谷歌分析(以下簡(jiǎn)稱GA),,需要在頁(yè)面中插入一段它提供的javascript片段,,這個(gè)片段往往被稱為埋點(diǎn)代碼,。下面是我的博客中所放置的谷歌分析埋點(diǎn)代碼截圖:

圖2. 谷歌分析埋點(diǎn)代碼

其中_gaq是GA的的全局?jǐn)?shù)組,,用于放置各種配置,,其中每一條配置的格式為:

  1. _gaq.push(['Action', 'param1', 'param2', ...]);

Action指定配置動(dòng)作,,后面是相關(guān)的參數(shù)列表,。GA給的默認(rèn)埋點(diǎn)代碼會(huì)給出兩條預(yù)置配置,,_setAccount用于設(shè)置網(wǎng)站標(biāo)識(shí)ID,,這個(gè)標(biāo)識(shí)ID是在注冊(cè)GA時(shí)分配的,。_trackPageview告訴GA跟蹤一次頁(yè)面訪問(wèn),。更多配置請(qǐng)參考:https://developers.google.com/analytics/devguides/collection/gajs/。實(shí)際上,,這個(gè)_gaq是被當(dāng)做一個(gè)FIFO隊(duì)列來(lái)用的,,配置代碼不必出現(xiàn)在埋點(diǎn)代碼之前,具體請(qǐng)參考上述鏈接的說(shuō)明,。

就本文來(lái)說(shuō),,_gaq的機(jī)制不是重點(diǎn),重點(diǎn)是后面匿名函數(shù)的代碼,,這才是埋點(diǎn)代碼真正要做的,。這段代碼的主要目的就是引入一個(gè)外部的js文件(ga.js),方式是通過(guò)document.createElement方法創(chuàng)建一個(gè)script并根據(jù)協(xié)議(http或https)將src指向?qū)?yīng)的ga.js,,最后將這個(gè)element插入頁(yè)面的dom樹(shù)上,。

注意ga.async = true的意思是異步調(diào)用外部js文件,即不阻塞瀏覽器的解析,,待外部js下載完成后異步執(zhí)行,。這個(gè)屬性是HTML5新引入的。

數(shù)據(jù)收集腳本執(zhí)行階段

數(shù)據(jù)收集腳本(ga.js)被請(qǐng)求后會(huì)被執(zhí)行,,這個(gè)腳本一般要做如下幾件事:

1,、通過(guò)瀏覽器內(nèi)置javascript對(duì)象收集信息,,如頁(yè)面title(通過(guò)document.title)、referrer(上一跳url,,通過(guò)document.referrer),、用戶顯示器分辨率(通過(guò)windows.screen)、cookie信息(通過(guò)document.cookie)等等一些信息,。

2,、解析_gaq收集配置信息。這里面可能會(huì)包括用戶自定義的事件跟蹤,、業(yè)務(wù)數(shù)據(jù)(如電子商務(wù)網(wǎng)站的商品編號(hào)等)等,。

3、將上面兩步收集的數(shù)據(jù)按預(yù)定義格式解析并拼接,。

4,、請(qǐng)求一個(gè)后端腳本,將信息放在http request參數(shù)中攜帶給后端腳本,。

這里唯一的問(wèn)題是步驟4,,javascript請(qǐng)求后端腳本常用的方法是ajax,但是ajax是不能跨域請(qǐng)求的,。這里ga.js在被統(tǒng)計(jì)網(wǎng)站的域內(nèi)執(zhí)行,而后端腳本在另外的域(GA的后端統(tǒng)計(jì)腳本是http://www./__utm.gif),,ajax行不通,。一種通用的方法是js腳本創(chuàng)建一個(gè)Image對(duì)象,將Image對(duì)象的src屬性指向后端腳本并攜帶參數(shù),,此時(shí)即實(shí)現(xiàn)了跨域請(qǐng)求后端,。這也是后端腳本為什么通常偽裝成gif文件的原因。通過(guò)http抓包可以看到ga.js對(duì)__utm.gif的請(qǐng)求:

圖3. 后端腳本請(qǐng)求的http包

可以看到ga.js在請(qǐng)求__utm.gif時(shí)帶了很多信息,,例如utmsr=1280×1024是屏幕分辨率,,utmac=UA-35712773-1是_gaq中解析出的我的GA標(biāo)識(shí)ID等等。

值得注意的是,,__utm.gif未必只會(huì)在埋點(diǎn)代碼執(zhí)行時(shí)被請(qǐng)求,,如果用_trackEvent配置了事件跟蹤,則在事件發(fā)生時(shí)也會(huì)請(qǐng)求這個(gè)腳本,。

由于ga.js經(jīng)過(guò)了壓縮和混淆,,可讀性很差,我們就不分析了,,具體后面實(shí)現(xiàn)階段我會(huì)實(shí)現(xiàn)一個(gè)功能類似的腳本,。

后端腳本執(zhí)行階段

GA的__utm.gif是一個(gè)偽裝成gif的腳本。這種后端腳本一般要完成以下幾件事情:

1,、解析http請(qǐng)求參數(shù)的到信息,。

2,、從服務(wù)器(WebServer)中獲取一些客戶端無(wú)法獲取的信息,如訪客ip等,。

3,、將信息按格式寫(xiě)入log。

5,、生成一副1×1的空gif圖片作為響應(yīng)內(nèi)容并將響應(yīng)頭的Content-type設(shè)為image/gif,。

5、在響應(yīng)頭中通過(guò)Set-cookie設(shè)置一些需要的cookie信息,。

之所以要設(shè)置cookie是因?yàn)槿绻櫸ㄒ辉L客,,通常做法是如果在請(qǐng)求時(shí)發(fā)現(xiàn)客戶端沒(méi)有指定的跟蹤cookie,則根據(jù)規(guī)則生成一個(gè)全局唯一的cookie并種植給用戶,,否則Set-cookie中放置獲取到的跟蹤cookie以保持同一用戶cookie不變(見(jiàn)圖4),。

圖4. 通過(guò)cookie跟蹤唯一用戶的原理

這種做法雖然不是完美的(例如用戶清掉cookie或更換瀏覽器會(huì)被認(rèn)為是兩個(gè)用戶),但是是目前被廣泛使用的手段,。注意,,如果沒(méi)有跨站跟蹤同一用戶的需求,可以通過(guò)js將cookie種植在被統(tǒng)計(jì)站點(diǎn)的域下(GA是這么做的),,如果要全網(wǎng)統(tǒng)一定位,,則通過(guò)后端腳本種植在服務(wù)端域下(我們待會(huì)的實(shí)現(xiàn)會(huì)這么做)。

系統(tǒng)的設(shè)計(jì)實(shí)現(xiàn)

根據(jù)上述原理,,我自己搭建了一個(gè)訪問(wèn)日志收集系統(tǒng),。總體來(lái)說(shuō),,搭建這個(gè)系統(tǒng)要做如下的事:

圖5. 訪問(wèn)數(shù)據(jù)收集系統(tǒng)工作分解

下面詳述每一步的實(shí)現(xiàn),。我將這個(gè)系統(tǒng)叫做MyAnalytics。

確定收集的信息

為了簡(jiǎn)單起見(jiàn),,我不打算實(shí)現(xiàn)GA的完整數(shù)據(jù)收集模型,,而是收集以下信息。

名稱 途徑 備注
訪問(wèn)時(shí)間 web server Nginx $msec
IP web server Nginx $remote_addr
域名 javascript document.domain
URL javascript document.URL
頁(yè)面標(biāo)題 javascript document.title
分辨率 javascript window.screen.height & width
顏色深度 javascript window.screen.colorDepth
Referrer javascript document.referrer
瀏覽客戶端 web server Nginx $http_user_agent
客戶端語(yǔ)言 javascript navigator.language
訪客標(biāo)識(shí) cookie
網(wǎng)站標(biāo)識(shí) javascript 自定義對(duì)象

埋點(diǎn)代碼

埋點(diǎn)代碼我將借鑒GA的模式,,但是目前不會(huì)將配置對(duì)象作為一個(gè)FIFO隊(duì)列用,。一個(gè)埋點(diǎn)代碼的模板如下:

這里我啟用了二級(jí)域名analytics.,統(tǒng)計(jì)腳本的名稱為ma.js,。當(dāng)然這里有一點(diǎn)小問(wèn)題,,因?yàn)槲也](méi)有https的服務(wù)器,所以如果一個(gè)https站點(diǎn)部署了代碼會(huì)有問(wèn)題,,不過(guò)這里我們先忽略吧,。

前端統(tǒng)計(jì)腳本

我寫(xiě)了一個(gè)不是很完善但能完成基本工作的統(tǒng)計(jì)腳本ma.js:

  1. (function () {
  2. var params = {};
  3. //Document對(duì)象數(shù)據(jù)
  4. if(document) {
  5. params.domain = document.domain || '';
  6. params.url = document.URL || '';
  7. params.title = document.title || '';
  8. params.referrer = document.referrer || '';
  9. }
  10. //Window對(duì)象數(shù)據(jù)
  11. if(window && window.screen) {
  12. params.sh = window.screen.height || 0;
  13. params.sw = window.screen.width || 0;
  14. params.cd = window.screen.colorDepth || 0;
  15. }
  16. //navigator對(duì)象數(shù)據(jù)
  17. if(navigator) {
  18. params.lang = navigator.language || '';
  19. }
  20. //解析_maq配置
  21. if(_maq) {
  22. for(var i in _maq) {
  23. switch(_maq[i][0]) {
  24. case '_setAccount':
  25. params.account = _maq[i][1];
  26. break;
  27. default:
  28. break;
  29. }
  30. }
  31. }
  32. //拼接參數(shù)串
  33. var args = '';
  34. for(var i in params) {
  35. if(args != '') {
  36. args += '&';
  37. }
  38. args += i + '=' + encodeURIComponent(params[i]);
  39. }
  40.  
  41. //通過(guò)Image對(duì)象請(qǐng)求后端腳本
  42. var img = new Image(1, 1);
  43. img.src = 'http://analytics./1.gif?' + args;
  44. })();

整個(gè)腳本放在匿名函數(shù)里,確保不會(huì)污染全局環(huán)境,。功能在原理一節(jié)已經(jīng)說(shuō)明,,不再贅述,。其中1.gif是后端腳本。

日志格式

日志采用每行一條記錄的方式,,采用不可見(jiàn)字符^A(ascii碼0x01,,Linux下可通過(guò)ctrl + v ctrl + a輸入,下文均用“^A”表示不可見(jiàn)字符0x01),,具體格式如下:

時(shí)間^AIP^A域名^AURL^A頁(yè)面標(biāo)題^AReferrer^A分辨率高^A分辨率寬^A顏色深度^A語(yǔ)言^A客戶端信息^A用戶標(biāo)識(shí)^A網(wǎng)站標(biāo)識(shí)

后端腳本

為了簡(jiǎn)單和效率考慮,,我打算直接使用nginx的access_log做日志收集,不過(guò)有個(gè)問(wèn)題就是nginx配置本身的邏輯表達(dá)能力有限,,所以我選用了OpenResty做這個(gè)事情,。OpenResty是一個(gè)基于Nginx擴(kuò)展出的高性能應(yīng)用開(kāi)發(fā)平臺(tái),內(nèi)部集成了諸多有用的模塊,,其中的核心是通過(guò)ngx_lua模塊集成了Lua,,從而在nginx配置文件中可以通過(guò)Lua來(lái)表述業(yè)務(wù)。關(guān)于這個(gè)平臺(tái)我這里不做過(guò)多介紹,,感興趣的同學(xué)可以參考其官方網(wǎng)站http:///,,或者這里有其作者章亦春(agentzh)做的一個(gè)非常有愛(ài)的介紹OpenResty的slide:http:///misc/slides/ngx-openresty-ecosystem/,關(guān)于ngx_lua可以參考:https://github.com/chaoslawful/lua-nginx-module,。

首先,,需要在nginx的配置文件中定義日志格式:

  1. log_format tick "$msec^A$remote_addr^A$u_domain^A$u_url^A$u_title^A$u_referrer^A$u_sh^A$u_sw^A$u_cd^A$u_lang^A$http_user_agent^A$u_utrace^A$u_account";

注意這里以u(píng)_開(kāi)頭的是我們待會(huì)會(huì)自己定義的變量,其它的是nginx內(nèi)置變量,。

然后是核心的兩個(gè)location:

  1. location /1.gif {
  2. #偽裝成gif文件
  3. default_type image/gif;
  4. #本身關(guān)閉access_log,,通過(guò)subrequest記錄log
  5. access_log off;
  6.  
  7. access_by_lua "
  8. -- 用戶跟蹤cookie名為_(kāi)_utrace
  9. local uid = ngx.var.cookie___utrace
  10. if not uid then
  11. -- 如果沒(méi)有則生成一個(gè)跟蹤cookie,算法為md5(時(shí)間戳+IP+客戶端信息)
  12. uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent)
  13. end
  14. ngx.header['Set-Cookie'] = {'__utrace=' .. uid .. '; path=/'}
  15. if ngx.var.arg_domain then
  16. -- 通過(guò)subrequest到/i-log記錄日志,,將參數(shù)和用戶跟蹤cookie帶過(guò)去
  17. ngx.location.capture('/i-log?' .. ngx.var.args .. '&utrace=' .. uid)
  18. end
  19. ";
  20.  
  21. #此請(qǐng)求不緩存
  22. add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT";
  23. add_header Pragma "no-cache";
  24. add_header Cache-Control "no-cache, max-age=0, must-revalidate";
  25.  
  26. #返回一個(gè)1×1的空gif圖片
  27. empty_gif;
  28. }
  29.  
  30. location /i-log {
  31. #內(nèi)部location,不允許外部直接訪問(wèn)
  32. internal;
  33.  
  34. #設(shè)置變量,,注意需要unescape
  35. set_unescape_uri $u_domain $arg_domain;
  36. set_unescape_uri $u_url $arg_url;
  37. set_unescape_uri $u_title $arg_title;
  38. set_unescape_uri $u_referrer $arg_referrer;
  39. set_unescape_uri $u_sh $arg_sh;
  40. set_unescape_uri $u_sw $arg_sw;
  41. set_unescape_uri $u_cd $arg_cd;
  42. set_unescape_uri $u_lang $arg_lang;
  43. set_unescape_uri $u_utrace $arg_utrace;
  44. set_unescape_uri $u_account $arg_account;
  45.  
  46. #打開(kāi)日志
  47. log_subrequest on;
  48. #記錄日志到ma.log,,實(shí)際應(yīng)用中最好加buffer,格式為tick
  49. access_log /path/to/logs/directory/ma.log tick;
  50.  
  51. #輸出空字符串
  52. echo '';
  53. }

要完全解釋這段腳本的每一個(gè)細(xì)節(jié)有點(diǎn)超出本文的范圍,,而且用到了諸多第三方ngxin模塊(全都包含在OpenResty中了),,重點(diǎn)的地方我都用注釋標(biāo)出來(lái)了,可以不用完全理解每一行的意義,,只要大約知道這個(gè)配置完成了我們?cè)谠硪还?jié)提到的后端邏輯就可以了,。

日志輪轉(zhuǎn)

真正的日志收集系統(tǒng)訪問(wèn)日志會(huì)非常多,時(shí)間一長(zhǎng)文件變得很大,,而且日志放在一個(gè)文件不便于管理,。所以通常要按時(shí)間段將日志切分,例如每天或每小時(shí)切分一個(gè)日志,。我這里為了效果明顯,,每一小時(shí)切分一個(gè)日志,。我是通過(guò)crontab定時(shí)調(diào)用一個(gè)shell腳本實(shí)現(xiàn)的,shell腳本如下:

  1. _prefix="/path/to/nginx"
  2. time=`date +%Y%m%d%H`
  3.  
  4. mv ${_prefix}/logs/ma.log ${_prefix}/logs/ma/ma-${time}.log
  5. kill -USR1 `cat ${_prefix}/logs/nginx.pid`

這個(gè)腳本將ma.log移動(dòng)到指定文件夾并重命名為ma-{yyyymmddhh}.log,,然后向nginx發(fā)送USR1信號(hào)令其重新打開(kāi)日志文件,。

然后再/etc/crontab里加入一行:

  1. 59 * * * * root /path/to/directory/rotatelog.sh

在每個(gè)小時(shí)的59分啟動(dòng)這個(gè)腳本進(jìn)行日志輪轉(zhuǎn)操作。

測(cè)試

下面可以測(cè)試這個(gè)系統(tǒng)是否能正常運(yùn)行了,。我昨天就在我的博客中埋了相關(guān)的點(diǎn),,通過(guò)http抓包可以看到ma.js和1.gif已經(jīng)被正確請(qǐng)求:

圖6. http包分析ma.js和1.gif的請(qǐng)求

同時(shí)可以看一下1.gif的請(qǐng)求參數(shù):

圖7. 1.gif的請(qǐng)求參數(shù)

相關(guān)信息確實(shí)也放在了請(qǐng)求參數(shù)中。

然后我tail打開(kāi)日志文件,,然后刷新一下頁(yè)面,,因?yàn)闆](méi)有設(shè)access log buffer, 我立即得到了一條新日志:

  1. 1351060731.360^A0.0.0.0^Awww.^Ahttp://www./^ACodingLabs^A^A1024^A1280^A24^Azh-CN^AMozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4^A4d612be64366768d32e623d594e82678^AU-1-1

注意實(shí)際上原日志中的^A是不可見(jiàn)的,,這里我用可見(jiàn)的^A替換為方便閱讀,,另外IP由于涉及隱私我替換為了0.0.0.0。

看一眼日志輪轉(zhuǎn)目錄,,由于我之前已經(jīng)埋了點(diǎn),,所以已經(jīng)生成了很多輪轉(zhuǎn)文件:

圖8. 輪轉(zhuǎn)日志

關(guān)于分析

通過(guò)上面的分析和開(kāi)發(fā)可以大致理解一個(gè)網(wǎng)站統(tǒng)計(jì)的日志收集系統(tǒng)是如何工作的。有了這些日志,,就可以進(jìn)行后續(xù)的分析了,。本文只注重日志收集,所以不會(huì)寫(xiě)太多關(guān)于分析的東西,。

注意,,原始日志最好盡量多的保留信息而不要做過(guò)多過(guò)濾和處理。例如上面的MyAnalytics保留了毫秒級(jí)時(shí)間戳而不是格式化后的時(shí)間,,時(shí)間的格式化是后面的系統(tǒng)做的事而不是日志收集系統(tǒng)的責(zé)任,。后面的系統(tǒng)根據(jù)原始日志可以分析出很多東西,例如通過(guò)IP庫(kù)可以定位訪問(wèn)者的地域,、user agent中可以得到訪問(wèn)者的操作系統(tǒng),、瀏覽器等信息,再結(jié)合復(fù)雜的分析模型,,就可以做流量,、來(lái)源、訪客,、地域,、路徑等分析了。當(dāng)然,,一般不會(huì)直接對(duì)原始日志分析,,而是會(huì)將其清洗格式化后轉(zhuǎn)存到其它地方,如MySQL或HBase中再做分析,。

分析部分的工作有很多開(kāi)源的基礎(chǔ)設(shè)施可以使用,,例如實(shí)時(shí)分析可以使用Storm,,而離線分析可以使用Hadoop。當(dāng)然,,在日志比較小的情況下,,也可以通過(guò)shell命令做一些簡(jiǎn)單的分析,例如,,下面三條命令可以分別得出我的博客在今天上午8點(diǎn)到9點(diǎn)的訪問(wèn)量(PV),,訪客數(shù)(UV)和獨(dú)立IP數(shù)(IP):

  1. awk -F^A '{print $1}' ma-2012102409.log | wc -l
  2. awk -F^A '{print $12}' ma-2012102409.log | uniq | wc -l
  3. awk -F^A '{print $2}' ma-2012102409.log | uniq | wc -l

其它好玩的東西朋友們可以慢慢挖掘。

參考

GA的開(kāi)發(fā)者文檔:https://developers.google.com/analytics/devguides/collection/gajs/

一篇關(guān)于實(shí)現(xiàn)nginx收日志的文章:http://blog./2011/11/?????¨nginxè?°??¥???

關(guān)于Nginx可以參考:http://wiki./Main

OpenResty的官方網(wǎng)站為:http://

ngx_lua模塊可參考:https://github.com/chaoslawful/lua-nginx-module

本文http抓包使用Chrome瀏覽器開(kāi)發(fā)者工具,,繪制思維導(dǎo)圖使用Xmind,,流程和結(jié)構(gòu)圖使用Tikz PGF

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多