說起nginx自定義access.log,可能大家都不陌生,有的同學會說,那不就是定義一下format, format里面可以使用nginx內置的變量$remoteaddr、$status、$httpuseragent、$timelocal...(更多nginx內置的變量) , 這種咱們就不說了,這個簡單,基本大家都會。
那是自定義access.log的名字? 比如在一個多個虛擬主機的nginx中,我們想根據不同的server去生成不同的access.log。這樣我們去查access.log的時候也方便,從名字一眼就能看出要去哪個文件去查。你也可以通過上面nginx內置的變量去給文件命名,比如我用server_name去命名,我就可以這樣寫:
log_format log/$http_host.access.log
然后reload一下nginx就可以了。當然我要說的也不是這個。
其實我想說的是這種:自定義logformat 在logformat里面添加一些自己自定義的變量(openresty群里一個朋友問到這個問題)。
比如我想給每個HTTP請求的access.log中添加一個UUID字符串。你可能要問這東西有什么用呢?你想一下,對於一些POST請求(也可能是GET請求),你從URL上看不出差別,但是因為POST的body太大,你又不想記到nginx的access.log里面,還有一種可能是你POST的body里面是二進制,即便你記錄到access.log里面了,等你遇到問題要去查的時候你就發現"然並卵啊",記錄這東西也沒啥用(是不是想哭),不用哭,今天就是介紹這個的。
我們可以通過一個唯一的字符串去標記一個請求,然后這個串可以通過HTTP的Header或者通過QueryString傳給我們的A\B\C\D...服務器,直到一個請求周期結束。然后我們在需要記錄log的地方可以將這個unique的串也記錄下來,如果你把日志都集中存儲到了一個地方(比如ELK),當你去查問題的時候,你可以通過這個字符串就搜索到了這個請求的整個生命周期,是不是有點爽呢。 不扯淡了,下面就說說怎么搞的,這里用了nginx和lua去做,至於為啥用lua,說因為任性可以不,其實就是為了玩。
如果沒聽過lua或者nginx,再如果不敢興趣,那只能說不好意思,這位客官,你可能只能觀一下了。想了解的同學可以自行搜索一下這倆玩意。如果已經了解了這倆東西不了解ngxlua(或者openresty)的也可以去搜索一下,這倆項目都是@agentzh (章亦春)春哥的大作。這里我為了簡單,就選用了ngxlua。
-
先說下lua生產隨機數字吧,我把代碼放到util.lua里面了
local os = require('os') local math = require('math') local io = require("io") local _M = {} _M.uuid = function() local template = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" d = io.open("/dev/urandom", "r"):read(4) math.randomseed(os.time() + d:byte(1) + (d:byte(2) * 256) + (d:byte(3) * 65536) + (d:byte(4) * 4294967296)) return string.gsub( template, "x",function (c) local v = (c == "x") and math.random(0, 0xf) or math.random(8, 0xb) return string.format("%x", v) end) end return _M
這里采用了從/dev/urandom去讀取隨機數,讀了前4位,然后根據時間、前四位字符的ascii分作為隨機數的種子,然后去隨機參數字符替換template里面的x,你也可以再加其他去作為隨機數發射器的種子比如pid,不過在lua中,你可能還需要posix這個module,最終生成一個32位的字符串。
-
那就直接亮出nginx的配置吧
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" "$uuid"'; access_log logs/access.log main buff=4k; sendfile on; tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; lua_package_path "$prefix/lua/?.lua;/test/lua/?.lua;"; init_by_lua_file lua/util.lua;
注意看,其他字段都沒改,懶得連main都沒改,只是最后添加了一個自定義的字段叫'"$uuid"', 還有你的luapackagepath以及initbylua_file位置一定要寫正確,不然會因為找不到文件報錯。然后看下相關server里面的配置。
server { listen 80 backlog=512; server_name localhost; charset utf-8; underscores_in_headers on; set_by_lua_block $uuid { local util = require('util') local uniq_id, _ = util.uuid() return uniq_id } location /redirect { return 301 http://127.1/lua; } location /lua { default_type "text/plain"; content_by_lua_block { local args = ngx.req.get_uri_args() for key, val in pairs(args) do if type(val) == "table" then ngx.say(key, ": ", table.concat(val, ", ")) else ngx.say(key, ": ", val) end end ngx.say(ngx.var.arg_b) } } ...
只貼這么多關鍵部分吧,然后去check一下配置是否有問題
[sky@10_211_55_6_VM_CENTOS go]> sudo /opt/soft/nginx/sbin/nginx -t nginx: [alert] lua_code_cache is off; this will hurt performance in /opt/soft/ nginx1.9.3/conf/nginx.conf:35 nginx: the configuration file /opt/soft/nginx1.9.3/conf/nginx.conf syntax is ok nginx: configuration file /opt/soft/nginx1.9.3/conf/nginx.conf test is successful
這個警告是因為開發階段luacodecache沒開,生產環境是一定要開的,為了我們修改lua代碼能及時生效。那我們去試試看下效果先
[sky@10_211_55_6_VM_CENTOS go]> curl -iL 'http://127.1/redirect?a=1&b=2&c=3' HTTP/1.1 301 Moved Permanently Server: nginx/1.9.3 Date: Thu, 05 Nov 2015 16:34:03 GMT Content-Type: text/html Content-Length: 184 Connection: keep-alive Location: http://127.1/lua
然后去看下access.log的內容
[sky@10_211_55_6_VM_CENTOS go]> tail -n 2 /opt/soft/nginx/logs/access.log 127.0.0.1 - - [05/Nov/2015:13:32:09 +0800] "GET /lua HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-" "8272ba28283350d51056995be1f0c244" 127.0.0.1 - - [05/Nov/2015:23:56:55 +0800] "GET /lua HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-" "118ab63e5bf8e8c0a2d04e7ab898120d"
-
透傳給應用層
-
如果你使用的是proxy模式你可以使用proxysetheader
proxy_set_header HTTP_UUID $uuid;
* 如果你通過fastcgi協議傳遞給后端的PHP,你可以使用fastcgi_param fastcgi_param HTTP_UUID $uuid;
注意:nginx對對header name的字符做了限制,默認 underscoresinheaders 為off,表示如果header name中包含下划線,則忽略掉。
解決辦法:
-
配置中http部分 增加underscoresinheaders on; 配置
-
用減號-替代下划線符號_,避免這種變態問題。