<p>Nginx 日志阶段不支持直接执行 Lua,需用 log_by_lua* 在日志生成前设置变量,再通过 log_format 引用;变量须合法命名、注意作用域与非阻塞限制,并可结合条件与脱敏提升实用性。</p>

Nginx 本身不支持在 log_phase(日志阶段)直接执行 Lua 脚本,ngx_lua 模块的 log_by_lua* 指令**确实运行在日志写入前的最后阶段**,但它属于“日志生成阶段”,而非 Nginx 内置的日志格式化与落盘阶段。因此,想在日志中记录详细调试信息,核心方式是:利用 log_by_lua* 设置变量或构造日志字段,再通过 log_format 引用这些变量。
1. 启用 log_by_lua* 并准备调试数据
确保已编译安装了 ngx_http_lua_module(如 OpenResty),并在配置中启用:
- 在
http或server块中使用log_by_lua_block或log_by_lua_file - 在其中访问请求上下文(
ngx.var、ngx.ctx、ngx.req.get_headers()等)收集调试信息 - 将关键信息存入自定义变量(如
ngx.var.log_debug_info),供日志格式引用
示例:
log_by_lua_block {
local headers = ngx.req.get_headers()
local debug_info = string.format("status=%d, upstream=%s, rt=%.3f, ua=%q",
ngx.status,
ngx.var.upstream_addr or "-",
tonumber(ngx.var.request_time) or 0,
headers["user-agent"] and headers["user-agent"]:sub(1, 64) or "-"
)
ngx.var.log_debug_info = debug_info
}2. 自定义 log_format 引用 Lua 设置的变量
log_format 必须定义在 http 块,并且变量名需与 ngx.var.xxx 中设置的完全一致:
- 变量必须是合法的 Nginx 变量名(仅含字母、数字、下划线,不能以数字开头)
- 若变量未被 Lua 设置,Nginx 默认输出空字符串或“-”,不会报错
- 建议用
$log_debug_info这类明确语义的名称,避免和内置变量冲突
示例:
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time" '
'debug="$log_debug_info"';3. 注意变量作用域与时机限制
log_by_lua* 是日志阶段唯一可执行 Lua 的入口,但有重要约束:
- 不能调用阻塞函数(如
ngx.sleep、ngx.socket.tcp()),否则会阻塞日志写入 - 无法修改响应体或状态码(此时响应已发出,只读上下文)
-
ngx.ctx在 log 阶段仍可用,适合传递 earlier phase(如 rewrite/access)中收集的数据 - 若需记录上游响应头或体,需在
header_filter_by_lua*或body_filter_by_lua*中缓存到ngx.ctx,再在log_by_lua*中读取
4. 实用技巧:结构化调试字段 + 条件记录
为避免日志膨胀,可结合 map 或条件逻辑控制调试信息粒度:
- 用
map定义开关变量(如$debug_mode),根据 header、IP 或参数动态启用 - 在
log_by_lua_block中判断ngx.var.debug_mode == "1"再拼接详细字段 - 对敏感字段(如 Authorization、Cookie)做脱敏处理,例如
string.gsub(val, "%S+", "xxx") - 用
ngx.escape_uri或string.format("%q")避免日志格式被特殊字符破坏


















