log_format精简不能消除cpu损耗,真正开销在于日志变量每次请求的求值与格式化;应删除非必要变量、避免伪拼接、优先用原生变量、禁用高开销解析类变量,并用数据验证效果。

精简 log_format 字段本身不能直接规避“路径字符串拼接”的 CPU 损耗——Nginx 的 log_format 是静态模板定义,不涉及运行时字符串拼接;真正消耗 CPU 的,是日志变量在每次请求中被求值、格式化、组合成最终日志行的过程。所谓“路径字符串拼接”,往往指误用 $request_uri、$uri、$document_uri 等变量,或在 log_format 中叠加多个需解析/截取/拼接的变量(如 "$scheme://$host$request_uri"),导致每次请求都触发冗余计算与内存分配。
只保留业务强相关字段,砍掉“好看但无用”的变量
每多一个变量,Nginx 就要执行一次查找、类型转换、字符串拷贝或子串提取。尤其像 $http_x_forwarded_for(需解析逗号分隔)、$upstream_http_x_trace_id(需跨模块查 header)、$request_time(浮点转字符串)这类变量,开销远高于 $status 或 $bytes_sent。
- 删除非排查必需的字段:比如
$server_name(通常固定)、$hostname(同 server)、$pipe(极少用于诊断) - 避免“伪结构化”拼接:不要写
'"$scheme $host$request_uri $server_protocol"',直接用$request——它已包含全部请求行,无需拼接 - 用原生变量替代手工构造:想记录完整 URL?优先用
$scheme://$host$uri$is_args$args而非"https://" $host $request_uri(后者在 HTTP 请求中会错拼 http)
慎用需运行时解析的变量,尤其是带正则或截取逻辑的
Nginx 在填充日志变量时,对 $arg_*、$cookie_*、$sent_http_* 等需遍历 header/map 的变量,以及 $request_uri(可能含解码逻辑)、$uri(经重写后路径)都会触发额外处理。更危险的是自定义 map 或 geoip 模块变量,它们可能隐式调用正则匹配或哈希查找。
- 不用
$request_uri替代$uri:前者是原始未解码路径,含特殊字符需转义;后者是内部标准化路径,更快更安全 - 避免在 log_format 中嵌套
map变量:如$geoip_country_code若依赖 GeoIP2 模块,每次请求都要查二叉树索引 - 禁用调试类变量:如
$body_bytes_sent在启用 gzip 时需等压缩完成才可得值,延迟日志写入;生产环境改用$bytes_sent
用 static 值替代动态变量,降低求值频率
某些字段实际是常量或极低频变更项,却在每条日志中重复求值。例如 $server_addr(监听 IP)、$time_iso8601(需格式化时间)、$msec(毫秒级浮点转字符串)都是高频开销源。
- 固定端口服务,用
"80"或"443"字面量代替$server_port - 时间戳若仅用于大致定位,可用
$time_local(Nginx 内部缓存的秒级字符串,零分配)替代$time_iso8601(每次生成新字符串) - 如需 trace 上下文,优先注入到请求 header(如
X-Request-ID),用$http_x_request_id直接取值,而非在日志中用 Lua 拼接生成
验证精简效果:看真实 CPU 占比与日志吞吐
优化不是拍脑袋删字段,而是用数据确认收益。高并发下,日志格式越精简,worker 进程的 sys 时间占比越低,单核 QPS 承载能力越高。
- 压测对比:用 wrk 或 go-wrk 对比开启前后
top -p $(pgrep nginx) -H中各线程的 %CPU 和 %sys - 检查日志行平均长度:
head -n 10000 access.log | awk '{sum+=length} END {print sum/NR}',下降 20% 以上说明冗余字段有效剔除 - 观察
nginx -T | grep log_format输出是否仍含已弃用变量——避免配置残留误导判断











