# 正確的記錄日志
看過本章第一節的同學應該還記得,log_by_lua是一個會話階段最后發生的,文件操作是阻塞的(FreeBSD直接無視),nginx為了實時高效的給請求方應答后,日志記錄是在應答后異步記錄完成的。由此可見如果我們有日志輸出的情況,最好統一到log_by_lua階段。如果我們自定義放在content_by_lua階段,那么將線性的增加請求處理時間。
在公司某個定制化項目中,nginx上的日志內容都要輸送到syslog日志服務器。我們使用了[lua-resty-logger-socket](https://github.com/cloudflare/lua-resty-logger-socket)這個庫。
> 調用示例代碼如下(有問題的):
~~~
-- lua_package_path "/path/to/lua-resty-logger-socket/lib/?.lua;;";
--
-- server {
-- location / {
-- content_by_lua 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的時候會將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
~~~
在實測過程中我們發現了些問題:
- 緩存無效:如果flush_limit的值稍大一些(例如 2000),會導致某些體積比較小的日志出現莫名其妙的丟失,所以我們只能把flush_limit調整的很小
- 自己拼寫msg所有內容,比較辛苦
那么我們來看[lua-resty-logger-socket](https://github.com/cloudflare/lua-resty-logger-socket)這個庫的log函數是如何實現的呢,代碼如下:
~~~
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階段變量的生命周期會隨著會話的終結而終結,所以當日志量小于flush_limit的情況下這些日志就不能被累積,也不會觸發_flush_buffer函數,所以小日志會丟失。
這些坑回頭看來這么明顯,所有的問題都是因為我們把lua/log.lua用錯階段了,應該放到log_by_lua階段,所有的問題都不復存在。
> 修正后:
~~~
lua_package_path "/path/to/lua-resty-logger-socket/lib/?.lua;;";
server {
location / {
content_by_lua lua/content.lua;
log_by_lua lua/log.lua;
}
}
~~~
這里有個新問題,如果我的log里面需要輸出一些content的臨時變量,兩階段之間如何傳遞參數呢?
> 方法肯定有,推薦下面這個:
~~~
location /test {
rewrite_by_lua '
ngx.say("foo = ", ngx.ctx.foo)
ngx.ctx.foo = 76
';
access_by_lua '
ngx.ctx.foo = ngx.ctx.foo + 3
';
content_by_lua '
ngx.say(ngx.ctx.foo)
';
}
~~~
更多有關ngx.ctx信息,請看[這里](http://wiki.nginx.org/HttpLuaModuleZh#ngx.ctx)。
- 序
- Lua簡介
- Lua環境搭建
- 基礎數據類型
- 表達式
- 控制結構
- if/else
- while
- repeat
- 控制結構for的使用
- break,return
- Lua函數
- 函數的定義
- 函數的參數
- 函數的返回值
- 函數回調
- 模塊
- String庫
- Table庫
- 日期時間函數
- 數學庫函數
- 文件操作
- 元表
- 面向對象編程
- FFI
- LuaRestyRedisLibrary
- select+set_keepalive組合操作引起的數據讀寫錯誤
- redis接口的二次封裝(簡化建連、拆連等細節)
- redis接口的二次封裝(發布訂閱)
- pipeline壓縮請求數量
- script壓縮復雜請求
- LuaCjsonLibrary
- json解析的異常捕獲
- 稀疏數組
- 空table編碼為array還是object
- 跨平臺的庫選擇
- PostgresNginxModule
- 調用方式簡介
- 不支持事務
- 超時
- 健康監測
- SQL注入
- LuaNginxModule
- 執行階段概念
- 正確的記錄日志
- 熱裝載代碼
- 阻塞操作
- 緩存
- sleep
- 定時任務
- 禁止某些終端訪問
- 請求返回后繼續執行
- 調試
- 調用其他C函數動態庫
- 我的lua代碼需要調優么
- 變量的共享范圍
- 動態限速
- shared.dict 非隊列性質
- 如何添加自己的lua api
- 正確使用長鏈接
- 如何引用第三方resty庫
- 使用動態DNS來完成HTTP請求
- 緩存失效風暴
- Lua
- 下標從1開始
- 局部變量
- 判斷數組大小
- 非空判斷
- 正則表達式
- 不用標準庫
- 虛變量
- 函數在調用代碼前定義
- 抵制使用module()函數來定義Lua模塊
- 點號與冒號操作符的區別
- 測試
- 單元測試
- API測試
- 性能測試
- 持續集成
- 灰度發布
- web服務
- API的設計
- 數據合法性檢測
- 協議無痛升級
- 代碼規范
- 連接池
- c10k編程
- TIME_WAIT問題
- 與Docker使用的網絡瓶頸
- 火焰圖
- 什么時候使用
- 顯示的是什么
- 如何安裝火焰圖生成工具
- 如何定位問題