nginx无法直接用log_format计算$body_bytes_sent与$request_time比值,需在日志中分别记录二者,再于解析阶段做除法估算用户端带宽(bytes/sec);必须同时启用$body_bytes_sent和$request_time,缺一不可。

不能直接用 log_format 计算 $body_bytes_sent 与请求耗时的比值,Nginx 日志模块不支持运行时数学运算。但可以通过记录这两个字段,在日志解析阶段做除法,间接实现“用户端带宽”(即响应体平均传输速率,单位:bytes/sec)的动态估算。
必须启用的两个关键变量
要还原单次请求的传输速率,日志中至少需包含:
- $body_bytes_sent:实际发给客户端的响应体字节数(不含响应头),是“传输了多少”的依据;
- $request_time:从读取第一个字节到发送完最后一个字节的总耗时(秒,精度为毫秒,如 0.123),是“用了多久”的依据。
二者缺一不可。若只记 $upstream_response_time,它不包含 Nginx 自身发送响应头/体的时间,会低估真实端到端延迟。
推荐的日志格式配置
在 http 块中定义专用格式,确保字段顺序清晰、分隔符明确(建议用制表符或固定分隔符):
log_format bandwidth_rate '$time_iso8601\t$uri\t$status\t$body_bytes_sent\t$request_time';access_log /var/log/nginx/bandwidth_rate.log bandwidth_rate buffer=64k flush=2s;
说明:
- 使用 \t 分隔可避免空格导致字段错位(尤其 URI 含空格时);
- buffer + flush 设置兼顾实时性与 I/O 性能,2 秒内延迟对带宽估算影响极小;
- 不建议在日志里硬编码计算(如 awk 表达式),Nginx 不解析也不执行。
日志解析时动态计算带宽(bytes/sec)
拿到日志后,用流式命令逐行计算并过滤有效值(排除 $body_bytes_sent=0 或 $request_time≤0 的无效请求):
tail -n 0 -f /var/log/nginx/bandwidth_rate.log | awk -F'\t' '$4>0 && $5>0 { printf "%s\t%s\t%.0f bytes/sec\n", $1, $2, $4/$5 }'输出示例:
2026-06-18T10:05:22+00:00 /api/report 200 12582912 bytes/sec进一步转换为 KB/s 或 Mbps:
- KB/s:把结果除以 1024;
- Mbps(兆比特每秒):(bytes/sec × 8) ÷ 1000000;
- 注意:这是单请求瞬时速率,不是连接持续速率,受 TCP 慢启动、网络抖动、客户端接收缓冲等影响,适合识别异常慢传或大文件低速下载场景。
实用注意事项
该方法反映的是“服务端视角的平均传输速率”,并非客户端真实带宽,但对运维排查很有价值:
- 若大量请求 $request_time 长而 $body_bytes_sent 小 → 可能卡在后端或 Nginx 处理逻辑;
- 若 $body_bytes_sent 大但 $request_time 短 → 说明传输快,网络通畅;
- 若同一资源(如大 JS 文件)在不同客户端 $body_bytes_sent/$request_time 差异极大 → 可能存在中间设备限速或链路质量差异;
- 不适用于 HTTP/2 多路复用下精确归因单个 stream 的带宽,因 $request_time 是整个请求生命周期,非纯传输窗口。











