看過本章第一節(jié)的同學(xué)應(yīng)該還記得,log_by_lua*
是一個(gè)請求經(jīng)歷的最后階段。由于記日志跟應(yīng)答內(nèi)容無關(guān),Nginx 通常在結(jié)束請求之后才更新訪問日志。由此可見,如果我們有日志輸出的情況,最好統(tǒng)一到 log_by_lua*
階段。如果我們把記日志的操作放在 content_by_lua*
階段,那么將線性的增加請求處理時(shí)間。
在公司某個(gè)定制化項(xiàng)目中,Nginx 上的日志內(nèi)容都要輸送到 syslog 日志服務(wù)器。我們使用了lua-resty-logger-socket這個(gè)庫。
調(diào)用示例代碼如下(有問題的):
-- lua_package_path "/path/to/lua-resty-logger-socket/lib/?.lua;;";
--
-- server {
-- location / {
-- content_by_lua_file lua/log.lua;
-- }
-- }
-- lua/log.lua
local logger = require "resty.logger.socket"
if not logger.initted() then
local ok, err = logger.init{
host = 'xxx',
port = 1234,
flush_limit = 1, --日志長度大于flush_limit的時(shí)候會將msg信息推送一次
drop_limit = 99999,
}
if not ok then
ngx.log(ngx.ERR, "failed to initialize the logger: ",err)
return
end
end
local msg = string.format(.....)
local bytes, err = logger.log(msg)
if err then
ngx.log(ngx.ERR, "failed to log message: ", err)
return
end
在實(shí)測過程中我們發(fā)現(xiàn)了些問題:
那么我們來看lua-resty-logger-socket這個(gè)庫的 log 函數(shù)是如何實(shí)現(xiàn)的呢,代碼如下:
function _M.log(msg)
...
if (debug) then
ngx.update_time()
ngx_log(DEBUG, ngx.now(), ":log message length: " .. #msg)
end
local msg_len = #msg
if (is_exiting()) then
exiting = true
_write_buffer(msg)
_flush_buffer()
if (debug) then
ngx_log(DEBUG, "Nginx worker is exiting")
end
bytes = 0
elseif (msg_len + buffer_size < flush_limit) then -- 歷史日志大小+本地日志大小小于推送上限
_write_buffer(msg)
bytes = msg_len
elseif (msg_len + buffer_size <= drop_limit) then
_write_buffer(msg)
_flush_buffer()
bytes = msg_len
else
_flush_buffer()
if (debug) then
ngx_log(DEBUG, "logger buffer is full, this log message will be "
.. "dropped")
end
bytes = 0
--- this log message doesn't fit in buffer, drop it
...
由于在 content_by_lua*
階段變量的生命周期會隨著請求的終結(jié)而終結(jié),所以當(dāng)日志量小于 flush_limit
的情況下這些日志就不能被累積,也不會觸發(fā) _flush_buffer
函數(shù),所以小日志會丟失。
這些坑回頭看來這么明顯,所有的問題都是因?yàn)槲覀儼?lua/log.lua
用錯(cuò)階段了,應(yīng)該放到 log_by_lua*
階段,所有的問題都不復(fù)存在。
修正后:
lua_package_path "/path/to/lua-resty-logger-socket/lib/?.lua;;";
server {
location / {
content_by_lua_file lua/content.lua;
log_by_lua_file lua/log.lua;
}
}
這里有個(gè)新問題,如果我的 log 里面需要輸出一些 content 的臨時(shí)變量,兩階段之間如何傳遞參數(shù)呢?
方法肯定有,推薦下面這個(gè):
location /test {
rewrite_by_lua_block {
ngx.say("foo = ", ngx.ctx.foo)
ngx.ctx.foo = 76
}
access_by_lua_block {
ngx.ctx.foo = ngx.ctx.foo + 3
}
content_by_lua_block {
ngx.say(ngx.ctx.foo)
}
}
更多有關(guān) ngx.ctx 信息,請看這里。