log_format必须在http块内定义,server或location中使用会报错;需用access_log显式引用格式名,常用变量如$remote_addr、$request_time等须严格区分大小写且拼写正确。

如何用 log_format 定义自定义日志格式
核心是修改 Nginx 配置中的 log_format 指令,它必须放在 http 块内(不能在 server 或 location 里),否则会报错 nginx: [emerg] "log_format" directive is not allowed here。
常见错误是把 log_format 写进 server 块里,结果 reload 失败。正确写法示例:
http {
log_format custom '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
<pre class="brush:php;toolbar:false;">server {
access_log /var/log/nginx/access.log custom;
# ...
}}
-
$request_time是请求处理总耗时(单位:秒,精度毫秒),$upstream_response_time是后端响应耗时(仅代理场景有效) - 字段之间建议用空格或固定分隔符(如
|),避免日志解析时歧义 - 所有变量必须以
$开头,拼写错误(如写成$request_time_)会导致该字段恒为空字符串,不报错但无意义
哪些变量能用?优先选 $http_* 和 $sent_http_*
Nginx 日志变量分三类:客户端传入的($http_*)、服务端发出的($sent_http_*)、内部状态($status, $uri 等)。想记录请求头里的 X-Forwarded-For 或 User-ID,必须用 $http_x_forwarded_for、$http_user_id —— 注意全部小写、下划线替代短横线。
-
$http_x_real_ip和$realip_remote_addr不同:$http_x_real_ip是请求头原始值,$realip_remote_addr是经real_ip模块处理后的可信客户端 IP(需先加载模块并配置set_real_ip_from) -
$sent_http_cache_control可记录响应头中的缓存策略,但若后端没返回该头,则字段为空;而$sent_http_content_type通常稳定存在 - 自定义请求头(如
X-Trace-ID)只要客户端发了,就能用$http_x_trace_id捕获,无需额外模块
access_log 的第三个参数控制日志行为
启用自定义格式后,access_log 指令的第三个参数不是可有可无的开关,它直接影响日志是否缓冲、是否异步写入。例如:
access_log /var/log/nginx/access.log custom buffer=16k flush=5s;
-
buffer=16k表示攒够 16KB 再写磁盘,减少 I/O;但进程崩溃时可能丢失最后一批日志 -
flush=5s强制每 5 秒刷一次缓冲区,平衡实时性与性能;不加此参数时,只靠 buffer 触发写入 - 若同时配置多个
access_log行(如一份标准格式 + 一份 debug 格式),每条都会独立生效,注意磁盘空间和性能开销 - 路径必须由 Nginx worker 进程有写权限,常见坑是日志目录属主为
root,导致nginx: [alert] could not open error log file
调试日志格式时别只看 curl,要查真实请求头
用 curl -H "X-User-ID: abc123" http://localhost/ 测试时,$http_x_user_id 能正常输出;但线上流量常经过 CDN 或 LB,它们可能剥离或改写请求头。最稳妥方式是临时加一条日志,把整个原始请求头打出来:
log_format debug '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_x_forwarded_for" "$http_x_user_id" '
'"$http_user_agent" "$http_referer" '
'headers:$http_accept,$http_accept_language';
- 上线前务必用
nginx -t验证语法,再nginx -s reload;直接 kill -HUP 可能因配置错误导致 worker 退出 - 如果发现某字段始终为空,先用
tcpdump或后端应用日志确认该头是否真实到达 Nginx,而不是盲目调配置 - 日志字段过多(尤其含长 header)会显著增大单行体积,影响日志轮转和分析工具性能,生产环境建议只保留必要字段
变量名大小写和下划线规则、log_format 的作用域、缓冲参数对丢日志的影响——这三点最容易被跳过,但出问题时最难排查。











