Nginx TCP代理(stream模块)通过log_format+access_log记录连接级日志,结合map变量或logrotate实现按日切割,流量统计依赖日志解析或telegraf等工具实时采集bytes_sent/bytes_received字段。

在 Nginx 中,TCP 代理(通过 stream 模块实现)本身不支持像 HTTP 模块那样内置的访问日志格式和变量,因此流量统计与日志切割需要借助外部工具或 Nginx 自身有限但可用的机制来完成。核心思路是:利用 log_format + access_log 在 stream 上记录连接级日志,再结合系统日志轮转工具(如 logrotate)或时间戳动态日志路径实现切割;流量统计则依赖日志解析或实时指标采集(如配合 Prometheus)。
启用 stream 模块的日志记录
Nginx 的 stream 块支持基础日志功能,但变量比 HTTP 少得多(例如没有请求体大小、响应状态码等)。常用变量包括:
-
$remote_addr:客户端 IP -
$server_addr:服务端监听 IP -
$server_port:服务端监听端口 -
$protocol:协议名(如 tcp) -
$status:连接状态(1 表示成功建立,0 表示失败) -
$bytes_sent和$bytes_received:连接关闭时收发字节数(Nginx 1.19.0+ 支持) -
$session_time:会话持续秒数(精度为秒)
配置示例(nginx.conf 的 stream 区域):
log_format stream_log '[$time_local] $remote_addr:$remote_port -> $server_addr:$server_port '
'status=$status bytes_sent=$bytes_sent bytes_received=$bytes_received '
'session_time=$session_time';
access_log /var/log/nginx/stream_access.log stream_log;
按时间自动切割日志文件
Nginx 本身不支持 stream 日志的 time-based 切割(如 access_log /path/log_$time_iso8601.log),但可通过以下两种方式实现:
-
使用 $time_iso8601 变量动态生成路径(需 Nginx ≥ 1.19.0):
支持在access_log路径中嵌入变量,例如:access_log /var/log/nginx/stream_$(date -d today +\%Y\%m\%d).log stream_log;不可行(shell 语法不被解析);但可写成:access_log /var/log/nginx/stream_$(date +\%Y\%m\%d).log stream_log;—— 这仍是错误的,Nginx 不执行 shell。
✅ 正确做法是用$time_iso8601或$time_local配合map构造日期目录,例如:map $time_iso8601 $log_date { ~^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2}) $year$month$day; } access_log /var/log/nginx/stream_$log_date.log stream_log;注意:
map必须定义在stream块外(如顶层或 http 块内),且该变量仅在日志写入时计算一次,不能做到“每小时新建文件”,但可做到“每日一个文件”。 -
依赖 logrotate 定期切割:
更稳定通用。配置/etc/logrotate.d/nginx-stream:/var/log/nginx/stream_access.log { daily missingok rotate 30 compress delaycompress notifempty create 0644 www-data www-data sharedscripts postrotate nginx -s reload > /dev/null 2>&1 || true endscript }注意:reload 会重新打开日志文件,确保新连接写入新文件。
统计 TCP 流量与连接行为
由于 stream 日志只记录连接粒度(非流式数据包),统计需基于日志解析或增强采集:
-
离线统计(推荐初用):
用 awk / Python 解析bytes_sent和bytes_received字段,例如统计每日总流量:awk '$10 ~ /^[0-9]+$/ {sent += $10; recv += $12} END {print "Sent:", sent, "Recv:", recv}' /var/log/nginx/stream_20240501.log也可用
goaccess(需自定义日志格式)或lnav做交互式分析。 -
实时监控(生产推荐):
集成nginx-module-vts(仅支持 HTTP)不行;改用nginx-plus的 stream 指标,或开源方案:
– 使用nginx-prometheus-exporter(需开启 stub_status,但 stream 不支持 stub_status);
– 更可行的是部署telegraf+tailinput 插件,实时读取 stream 日志并提取字段,推送到 InfluxDB 或 Prometheus;
– 或用filebeat+ Logstash 做结构化解析后聚合。
注意事项与常见坑
实际部署中容易忽略的关键点:
-
stream日志默认不继承http块中的log_format,必须在stream块或顶层显式定义; -
$bytes_sent/$bytes_received是连接关闭时才写入,短连接(如 DNS 查询)可能值为 0;若连接异常中断(如客户端断开),这些字段可能不准确; - 高并发下频繁
nginx -s reload可能引发连接中断,logrotate 中建议用copytruncate替代 reload(但会丢失少量日志); - 日志路径需确保 Nginx worker 进程有写权限,尤其使用 systemd 时要注意
ProtectSystem=full等安全限制; - 如果需按客户端 IP 统计流量,注意
$remote_addr在有 LVS/四层负载时可能是真实 IP,也可能是前端代理 IP,无法像 HTTP 那样靠X-Forwarded-For补偿。


















