你如何測(cè)試和調(diào)試你的代碼呢?Lua 的兩個(gè)主力作者是這樣回復(fù)的:
Luiz Henrique de Figueiredo:我主要是一塊一塊的構(gòu)建,分塊測(cè)試。我很少使用調(diào)試器。即使用調(diào)試器,也只是調(diào)試 C 代碼。我從不用調(diào)試器調(diào)試 Lua 代碼。對(duì)于 Lua 來(lái)說(shuō),在適當(dāng)?shù)奈恢梅艓讞l打印語(yǔ)句通常就可以勝任了。
Roberto Ierusalimschy:我差不多也是這樣。當(dāng)我使用調(diào)試器時(shí),通常只是用來(lái)查找代碼在哪里崩潰了。對(duì)于 C 代碼,有個(gè)像 Valgrind 或者 Purify 這樣的工具是必要的。
摘自《編程之魂 -- 采訪 Lua 發(fā)明人的一篇文章》。
由此可見掌握日志輸出是多么重要,下至入門同學(xué),上至 Lua 作者,使用日志輸出來(lái)確定問(wèn)題,是很必要的基本手段。
OpenResty 的標(biāo)準(zhǔn)日志輸出原句為 ngx.log(log_level, ...)
,幾乎可以在任何 ngx_lua 階段進(jìn)行日志的輸出。
請(qǐng)看下面的示例:
#user nobody;
worker_processes 1;
error_log logs/error.log error; # 日志級(jí)別
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
content_by_lua_block {
local num = 55
local str = "string"
local obj
ngx.log(ngx.ERR, "num:", num)
ngx.log(ngx.INFO, " string:", str)
print([[i am print]])
ngx.log(ngx.ERR, " object:", obj)
}
}
}
}
訪問(wèn)網(wǎng)頁(yè),生成日志(logs/error.log 文件)結(jié)果如下:
2016/01/22 16:43:34 [error] 61610#0: *10 [lua] content_by_lua(nginx.conf:26):5:
num:55, client: 127.0.0.1, server: , request: "GET /hello HTTP/1.1",
host: "127.0.0.1"
2016/01/22 16:43:34 [error] 61610#0: *10 [lua] content_by_lua(nginx.conf:26):7:
object:nil, client: 127.0.0.1, server: , request: "GET /hello HTTP/1.1",
host: "127.0.0.1"
大家可以在單行日志中獲取很多有用的信息,例如:時(shí)間、日志級(jí)別、請(qǐng)求ID、錯(cuò)誤代碼位置、內(nèi)容、客戶端 IP 、請(qǐng)求參數(shù)等等,這些信息都是環(huán)境信息,可以用來(lái)輔助完成更多其他操作。當(dāng)然我們也可以根據(jù)自己需要定義日志格式,具體可以參考 nginx 的 log_format 章節(jié)。
細(xì)心的讀者發(fā)現(xiàn)了,中間的兩行日志哪里去了?這里不賣關(guān)子,其實(shí)是日志輸出級(jí)別的原因。上面的例子,日志輸出級(jí)別使用的 error,只有等于或大于這個(gè)級(jí)別的日志才會(huì)輸出。這里還有一個(gè)知識(shí)點(diǎn)就是 OpenResty 里面的 print
語(yǔ)句是 INFO 級(jí)別。
有關(guān) Nginx 的日志級(jí)別,請(qǐng)看下表:
ngx.STDERR -- 標(biāo)準(zhǔn)輸出
ngx.EMERG -- 緊急報(bào)錯(cuò)
ngx.ALERT -- 報(bào)警
ngx.CRIT -- 嚴(yán)重,系統(tǒng)故障,觸發(fā)運(yùn)維告警系統(tǒng)
ngx.ERR -- 錯(cuò)誤,業(yè)務(wù)不可恢復(fù)性錯(cuò)誤
ngx.WARN -- 告警,業(yè)務(wù)中可忽略錯(cuò)誤
ngx.NOTICE -- 提醒,業(yè)務(wù)比較重要信息
ngx.INFO -- 信息,業(yè)務(wù)瑣碎日志信息,包含不同情況判斷等
ngx.DEBUG -- 調(diào)試
他們是一些常量,越往上等級(jí)越高。讀者朋友可以嘗試把 error log 日志級(jí)別修改為 info,然后重新執(zhí)行一下測(cè)試用例,就可以看到全部日志輸出結(jié)果了。
對(duì)于應(yīng)用開發(fā),一般使用 ngx.INFO 到 ngx.CRIT 就夠了。生產(chǎn)中錯(cuò)誤日志開啟到 error 級(jí)別就夠了。如何正確使用這些級(jí)別呢?可能不同的人、不同的公司可能有不同見解。
如果你的日志需要?dú)w集,并且對(duì)時(shí)效性要求比較高那么這里要推薦的庫(kù)可能就讓你很喜歡了。 lua-resty-logger-socket ,可以說(shuō)很好的解決了上面提及的幾個(gè)特性。
lua-resty-logger-socket 的目標(biāo)是替代 Nginx 標(biāo)準(zhǔn)的 ngx_http_log_module 以非阻塞 IO 方式推送 access log 到遠(yuǎn)程服務(wù)器上。對(duì)遠(yuǎn)程服務(wù)器的要求是支持 syslog-ng 的日志服務(wù)。
引用官方示例:
lua_package_path "/path/to/lua-resty-logger-socket/lib/?.lua;;";
server {
location / {
log_by_lua_block {
local logger = require "resty.logger.socket"
if not logger.initted() then
local ok, err = logger.init{
host = 'xxx',
port = 1234,
flush_limit = 1234,
drop_limit = 5678,
}
if not ok then
ngx.log(ngx.ERR, "failed to initialize the logger: ",
err)
return
end
end
-- construct the custom access log message in
-- the Lua variable "msg"
local bytes, err = logger.log(msg)
if err then
ngx.log(ngx.ERR, "failed to log message: ", err)
return
end
}
}
}
例舉幾個(gè)好處: