stream模块access_log按连接粒度记录,每次tcp/udp连接结束或超时后写入一行日志;需启用--with-stream和--with-stream_realip_module(如需真实客户端ip),log_format定义在stream块顶层,access_log置于server块内,并使用$remote_addr、$upstream_addr、$session_time等专用变量。

stream 模块的 access_log 不是记录 HTTP 请求那种“每条请求一行”的日志,而是按连接粒度记录——每次 TCP/UDP 连接建立并结束(或超时断开)后,写入一行日志。它能捕获客户端 IP、后端地址、连接耗时、收发字节数等关键元数据,但前提是配置位置正确、变量可用、模块已启用。
必须满足的前提条件
缺一不可:
- Nginx 编译时启用
--with-stream(否则 stream 块直接报错) - 若需真实客户端 IP(比如前端有 LVS 或四层代理),还需启用
--with-stream_realip_module并配合set_real_ip_from和real_ip_header -
log_format必须定义在stream { }块顶层,不能放在server或upstream内部 -
access_log必须写在server { }块内,且日志路径所在目录要有 Nginx worker 进程的写权限
可用的核心变量与典型日志格式
stream 模块提供专用变量,和 http 模块不通用。常用且实用的包括:
-
$remote_addr:发起连接的客户端 IP(未启用 realip 时可能是上一跳代理 IP) -
$remote_port:客户端源端口 -
$server_addr和$server_port:Nginx 监听的服务地址与端口 -
$upstream_addr:实际转发到的后端地址(多个后端用逗号分隔,如10.0.1.5:3306, 10.0.1.6:3306) -
$session_time:连接持续时间(单位秒,精度毫秒,Nginx 1.13.9+ 支持) -
$upstream_bytes_sent/$upstream_bytes_received:发给后端 / 从后端收到的字节数 -
$time_iso8601:标准时间戳,比$time_local更利于日志分析
示例日志格式(放在 stream { } 顶部):
log_format stream_conn '$time_iso8601|$remote_addr:$remote_port|$server_addr:$server_port|'$upstream_addr|$session_time|$upstream_bytes_sent|$upstream_bytes_received';
使用tbot机器ID身份文件配合tsh CLI,通过Teleport访问控制SSH登录托管主机或执行远程命令。
如何在 server 块中启用并验证
在对应监听服务的 server { } 中添加 access_log 指令:
server {
listen 3307;
proxy_pass mysql_cluster;
access_log /var/log/nginx/mysql-tcp.log stream_conn;
}
注意:access_log 是 per-connection 日志,不是 per-packet。一次 MySQL 客户端连接执行多条 SQL,只记一行;连接空闲超时断开,也会触发写入一行。
验证是否生效:重启 Nginx 后,用 telnet 或 mysql-client 连一次目标端口,然后检查日志文件是否新增一行,内容是否包含预期字段(如时间、IP、耗时等)。
常见问题与避坑点
-
日志为空或格式错误:检查
log_format是否写在stream块外(比如误放到了http块里),或变量名拼错(如把$session_time写成$connect_time) -
客户端 IP 总是显示为 127.0.0.1 或代理 IP:确认是否启用了
stream_realip_module,并在server块中配置了set_real_ip_from和real_ip_header proxy_protocol(如果上游支持 PROXY 协议) -
日志文件没生成或权限拒绝:确保
/var/log/nginx/目录属主为 Nginx 运行用户(如www-data或nginx),且有写权限;也可临时改用/tmp/路径快速验证 -
连接耗时始终为 0 或极小值:
$session_time在连接刚建立就断开(如握手失败、认证拒绝)时可能来不及统计,属于正常行为;稳定长连接下该值才具参考价值










