nginx access_log要发挥价值,必须先用log_format在http块中定义格式名称(如main),再由access_log指令引用该名称;直接在access_log后拼接变量字符串会报错;不同分析工具(goaccess、ngxtop、filebeat)需匹配对应格式模板或启用json解析配置,且变量有效性依赖实际场景(如$upstream_response_time仅在proxy_pass下有值)。

要让 Nginx 的 access_log 真正发挥价值,关键不只在记录,更在于能否被日志分析工具准确识别和解析。格式不匹配,再全的日志也是废数据。
必须先声明 log_format,再引用名称
所有自定义格式都得在 http 块里用 log_format 定义一个名字,比如 main 或 json;access_log 指令只能填路径 + 这个名字。直接把变量拼成字符串塞进 access_log 后面,Nginx 会报错 “invalid number of arguments”。
- 正确写法:
log_format main '$remote_addr "$request" $status $request_time';+access_log /var/log/nginx/access.log main; - 错误写法:
access_log /var/log/nginx/access.log '$remote_addr "$request" $status';(语法不支持)
不同工具对格式的识别逻辑差异明显
GoAccess、ngxtop、Filebeat 这些工具不是“万能解析器”,它们依赖你告诉它怎么切字段。默认只认 COMBINED 或 COMMON 这类固定结构,一旦加了 $request_time、$upstream_response_time 或转成 JSON,就得显式配置。
- GoAccess:含扩展字段时必须用
--log-format手动写模板,例如%h "%r" %s %T对应 IP、请求行、状态码、耗时 - ngxtop:能自动读取 Nginx 配置里的
log_format定义,但需确保它能访问到 nginx.conf 文件路径 - Filebeat:JSON 日志要开启
json.keys_under_root: true,否则只会当纯文本处理,字段全丢在message字段里
变量可用性决定日志是否“有效”
不是所有变量都能随便往日志里加——有些只在特定场景下有值,否则输出短横线 -,反而干扰分析。
-
$upstream_response_time:只有配置了proxy_pass的 location 才有值,静态文件服务里永远是- -
$http_x_forwarded_for:未经set_real_ip_from配置校验,内容不可信,日志里记了也白记 -
$request_body:默认不记录,开启后性能开销大,一般只在调试接口时临时启用
推荐几种生产环境常用兼容组合
兼顾可读性、工具兼容性和排障需要,不必追求字段堆砌,够用就好。
- 通用分析(GoAccess/ngxtop):
log_format combined '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time'; - 对接 ELK/Filebeat:
log_format json '{"time":"$time_iso8601","ip":"$remote_addr","uri":"$uri","status":$status,"rt":$request_time}';+access_log /var/log/nginx/access.json json; - 安全审计精简版:
log_format audit '$time_iso8601 $remote_addr "$request_method $uri" $status $body_bytes_sent $http_user_agent';











