log_by_lua是最常用、最安全的打点阶段,因在响应发出后执行,保证数据完整可靠且不影响性能;适合采集状态码、耗时等指标并上报至Prometheus或StatsD。

在 OpenResty 环境中,log_by_lua 是最常用、最安全的打点阶段——它在请求处理完成、响应已发出后执行,不影响性能和响应逻辑,特别适合采集业务指标(如成功数、耗时、错误码分布等)并上报到 Prometheus、StatsD 或自建日志系统。
为什么选 log_by_lua 而不是 access_by_lua?
access_by_lua 在权限校验、路由匹配前执行,可能因鉴权失败、限流拦截等提前退出,导致打点丢失;而 log_by_lua 保证只要 Nginx 接收过该请求(哪怕返回 401/403/500),就一定会运行,数据更完整可靠。
同时,它不参与响应生成,不会阻塞 client,对吞吐影响极小。
基础打点:记录请求状态与耗时
OpenResty 默认提供 ngx.var.upstream_response_time、ngx.var.status、ngx.var.request_time 等变量,可直接用于统计:
- 用
ngx.var.request_time获取总处理时间(秒,精度为毫秒) - 用
ngx.var.status获取最终 HTTP 状态码(如 200、404、500) - 用
ngx.var.uri或ngx.var.args提取关键路径或参数(注意脱敏)
示例(写入本地日志):
log_by_lua_block {local status = tonumber(ngx.var.status)
local cost_ms = math.floor(tonumber(ngx.var.request_time) * 1000)
local uri = ngx.var.uri
ngx.log(ngx.INFO, string.format("metric: uri=%s status=%d cost_ms=%d", uri, status, cost_ms))
}
进阶打点:聚合计数 + 上报 StatsD/Prometheus
高频打点不宜每请求都 IO 写磁盘或发网络。推荐方式:
- 使用
lua_shared_dict做内存计数器(线程安全、进程内共享) - 配合定时器(
ngx.timer.at)或采样(如每 100 次 flush 一次)批量上报 - 对接 StatsD(UDP)或通过
resty-prometheus暴露 /metrics 接口
例如,统计各接口成功率:
lua_shared_dict api_metrics 10m;log_by_lua_block {
local dict = ngx.shared.api_metrics
local key = "status:" .. ngx.var.uri
dict:incr(key .. ":" .. ngx.var.status, 1)
dict:incr(key .. ":total", 1)
}
注意事项与避坑指南
-
禁止在 log_by_lua 中调用阻塞操作:如
os.execute、同步 socket、无超时的 http request -
避免变量未定义报错:用
tonumber(ngx.var.xxx) or 0或ngx.var.xxx == nil and "default" or ngx.var.xxx -
敏感信息必须脱敏:不要记录
ngx.var.args全量,应只提取必要字段(如ngx.req.get_uri_args().order_id),且过滤 token、phone、id_card 等 -
注意 shared_dict 容量和过期:设置合理大小,必要时用
dict:expire(key, 60)防止内存泄漏

















