Nginx 配置埋點js日志采集


 

頁面埋點&nginx日志采集

頁面(web容器:httpd/nginx負載均衡 + apache server)<===> 日志采集服務器(nginx服務器)

  1. 通過某個頁面跳轉到我們的頁面;
  2. 我們頁面一渲染完成加載埋點的js,執行業務邏輯采集信息;
  3. 采集頁面完成之后,訪問log.gif,把參數拼接在args發送給采集服務器;
  4. 采集服務器返回一個1*1空的圖片,斷開連接。

采集頁面埋點(在頁面body最后埋js)

<script type="text/javascript">
    var _maq = _maq || [];
    _maq.push(['_setAccount', '網站標識']);

    (function() {
        var ma = document.createElement('script'); 
        ma.type = 'text/javascript';
        ma.async = true;
        ma.src = 'http://xxxxxx/ma.js';
        var s = document.getElementsByTagName('script')[0]; 
        s.parentNode.insertBefore(ma, s);
    })();
</script>

采集服務器ma.js

(function () {
    var params = {};
    //Document對象數據
    if(document) {
        params.domain = document.domain || ''; 
        params.url = document.URL || ''; 
        params.title = document.title || ''; 
        params.referrer = document.referrer || ''; 
    }   
    //Window對象數據
    if(window && window.screen) {
        params.sh = window.screen.height || 0;
        params.sw = window.screen.width || 0;
        params.cd = window.screen.colorDepth || 0;
    }   
    //navigator對象數據
    if(navigator) {
        params.lang = navigator.language || ''; 
    }   
    //解析_maq配置
    if(_maq) {
        for(var i in _maq) {
            switch(_maq[i][0]) {
                case '_setAccount':
                    params.account = _maq[i][1];
                    break;
                default:
                    break;
            }   
        }   
    }   
    //拼接參數串
    var args = ''; 
    for(var i in params) {
        if(args != '') {
            args += '&';
        }   
        args += i + '=' + encodeURIComponent(params[i]);
    }   

    //通過Image對象請求后端腳本
    var img = new Image(1, 1); 
    img.src = 'http://xxxxxx/log.gif?' + args;
})();

日志格式

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

  時間^AIP^A域名^AURL^A頁面標題^AReferrer^A分辨率高^A分辨率寬^A顏色深度^A語言^A客戶端信息^A用戶標識^A網站標識

后端腳本

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

若傳統的啟動nginx出現如下異常錯誤: unknown directive "access_by_lua"

之所以報錯是缺少nginx的三方插件 

安裝一個ngx_openresty  該集成包中有:NginxLuaLuajitngx_lua,以及一些有用的Nginx第三方模塊。

安裝步驟如下:

# 下載
wget https://openresty.org/download/openresty-1.13.6.2.tar.gz
# 解壓
tar -zxf openresty-1.13.6.2.tar.gz
# 安裝
cd openresty-1.13.6.1

./configure --with-luajit   
make   
make install  

#安裝完成默認路徑
cd /usr/local/openresty

# 配置nginx服務器

 

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

log_format tick escape=json "$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_開頭的是我們待會會自己定義的變量,其它的是nginx內置變量。

采集服務器nginx配置 

 在nginx 1.11.8 以上版本中log_format 增加了escape=json 參數,在配置日志格式時加上轉義中文。

worker_processes  2;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format tick escape=json "$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";

    access_log  logs/access.log  tick;

    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        location /log.gif {
            #偽裝成gif文件
            default_type image/gif;    
            #本身關閉access_log,通過subrequest記錄log
            access_log off;

            access_by_lua "
                -- 用戶跟蹤cookie名為__utrace
                local uid = ngx.var.cookie___utrace        
                if not uid then
                    -- 如果沒有則生成一個跟蹤cookie,算法為md5(時間戳+IP+客戶端信息)
                    uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent)
                end 
                ngx.header['Set-Cookie'] = {'__utrace=' .. uid .. '; path=/'}
                if ngx.var.arg_domain then
                -- 通過subrequest到/i-log記錄日志,將參數和用戶跟蹤cookie帶過去
                    ngx.location.capture('/i-log?' .. ngx.var.args .. '&utrace=' .. uid)
                end 
            ";  

            #此請求不緩存
            add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT";
            add_header Pragma "no-cache";
            add_header Cache-Control "no-cache, max-age=0, must-revalidate";

            #返回一個1×1的空gif圖片
            empty_gif;
        }   

        location /i-log {
            #內部location,不允許外部直接訪問
            internal;

            #設置變量,注意需要unescape
            set_unescape_uri $u_domain $arg_domain;
            set_unescape_uri $u_url $arg_url;
            set_unescape_uri $u_title $arg_title;
            set_unescape_uri $u_referrer $arg_referrer;
            set_unescape_uri $u_sh $arg_sh;
            set_unescape_uri $u_sw $arg_sw;
            set_unescape_uri $u_cd $arg_cd;
            set_unescape_uri $u_lang $arg_lang;
            set_unescape_uri $u_utrace $arg_utrace;
            set_unescape_uri $u_account $arg_account;

            #打開日志
            log_subrequest on;
            #記錄日志到ma.log,實際應用中最好加buffer,格式為tick  這個地方的nginx_logs文件夾 需要先創建好 mkdir /usr/local/openresty/nginx/nginx_logs
            access_log nginx_logs/ma.log tick;

            #輸出空字符串
            echo '';
        }
    }
}

 

要完全解釋這段腳本的每一個細節有點超出本文的范圍,而且用到了諸多第三方ngxin模塊(全都包含在OpenResty中了),重點的地方我都用注釋標出來了,可以不用完全理解每一行的意義,只要大約知道這個配置完成了我們在原理一節提到的后端邏輯就可以了。

 

測試

點擊跳轉需要埋點的頁面

埋點的頁面

 

 

 埋點的頁面 ,可以看到已經請求了 http://xxx/log.gif 了

已經寫入日志了。成功


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM