动态会话日志记录 ngx_stream_log_module
一、引言
在使用 NGINX Stream 模块处理 TCP/UDP 会话时,我们常常需要记录每次连接的关键指标(如客户端 IP、会话时长、收发字节数等),便于后续的审计、监控和故障排查。ngx_stream_log_module
就是专门为此设计的,它允许你以自定义格式记录 TCP/UDP 会话日志,并提供了缓冲写入和文件描述符缓存等多种优化手段。
本文将以零基础、一步步的方式,带你从环境准备、模块验证,到配置示例、测试验证,再到常见故障排查,全面掌握 ngx_stream_log_module
。
二、模块概览
-
模块名称:
ngx_stream_log_module
-
首次引入:NGINX 1.11.4
-
作用:为 Stream(TCP/UDP)会话写入日志,支持自定义格式、日志缓冲、压缩及条件记录。
-
核心指令:
log_format
:定义日志格式access_log
:开启会话日志并指定格式、路径、缓冲、压缩、条件等open_log_file_cache
:为带变量的日志路径缓存文件描述符
三、典型场景
- 数据库代理审计
记录来自各客户端 IP 的 MySQL/TCP 连接时长和流量,用于安全审计。 - 实时流量监控
对 Redis、Kafka 等服务的 TCP 会话进行统计,监测异常流量突增。 - 故障定位
当下游服务异常时,通过$status
、$session_time
等指标快速定位问题来源。
四、先决条件
-
NGINX 支持 Stream 模块
请确保在编译或安装时包含了--with-stream
参数。 -
确认已编译或加载
ngx_stream_log_module
nginx -V 2>&1 | grep --color stream_log
-
日志目录准备
为防止权限问题,建议提前创建并设置好目录权限:sudo mkdir -p /spool/logs sudo chown nginx:nginx /spool/logs
五、指令详解
1. log_format
log_format name [escape=default|json|none] string ...;
- name:日志格式名称,用于
access_log
引用。 - escape:可选
default
(转义控制字符)、json
(JSON 专用转义)、none
(不转义)。 - string:日志模板,支持内嵌多个变量,如
$remote_addr
、$session_time
等。
内置变量示例:
变量 | 含义 |
---|---|
$remote_addr | 客户端 IP 地址 |
$time_local | 本地时间(日志记录时刻) |
$protocol | 会话使用的协议(如 TCP/UDP) |
$status | 连接结束状态码(0 表示正常) |
$bytes_sent | 发送给客户端的总字节数 |
$bytes_received | 从客户端接收的总字节数 |
$session_time | 会话时长,单位秒(带毫秒精度) |
2. access_log
access_log path format [buffer=size] [gzip[=level]] [flush=time] [if=condition];
access_log off;
- path:日志文件路径,可写死也可包含变量(见
open_log_file_cache
)。 - format:引用
log_format
定义的格式名。 - buffer:启用缓冲写入,单位字节(如
32k
);不超过系统原子写入大小。 - gzip:启用压缩写入,可选
=1..9
压缩级别;需编译时链接 zlib。 - flush:最大刷新间隔(如
5m
),超时或缓冲满后写盘。 - if:条件表达式;当条件为“0”或空字符串时,跳过本次日志记录。
3. open_log_file_cache
open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
- max:缓存的最大文件描述符数量(LRU 关闭最少使用的)。
- inactive:若某文件在此时长内未被访问,则关闭描述符(默认 10s)。
- min_uses:在
inactive
时间内最少使用次数,低于则不缓存(默认 1)。 - valid:每隔该时长检查文件名是否仍指向同一文件(默认 60s)。
六、配置示例(一步一步)
以下示例演示如何在 Stream 模块中记录会话日志,并对日志性能进行优化。
-
在
stream { ... }
块外全局定义文件描述符缓存stream {# 缓存最多 1000 个文件描述符,20s 不活跃则关闭,文件存在校验 1m,至少访问 2 次才缓存open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;… }
-
定义日志格式
stream {log_format basic '$remote_addr [$time_local] ''$protocol $status $bytes_sent $bytes_received ''$session_time';… }
-
开启会话日志
stream {# 将日志缓冲到 32KB,5 分钟或满缓冲时写盘access_log /spool/logs/nginx-access.log basic buffer=32k flush=5m;… }
-
启用压缩(可选)
stream {# 以 gzip 压缩(级别 2),缓冲同上access_log /spool/logs/nginx-access.log.gz basic gzip=2 buffer=64k flush=5m;… }
-
条件日志(可选)
stream {# 仅记录时长超过 10 秒的会话access_log /spool/logs/long-sessions.log basic if=$session_time\>10;… }
完整示例整合:
stream {# 1. 文件描述符缓存open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;# 2. 日志格式log_format basic '$remote_addr [$time_local] ''$protocol $status $bytes_sent $bytes_received ''$session_time';# 3. 普通日志:32k 缓冲,5m 刷新access_log /spool/logs/nginx-access.log basic buffer=32k flush=5m;# 4. 压缩日志:64k 缓冲,gzip 2,5m 刷新access_log /spool/logs/nginx-access.log.gz basic gzip=2 buffer=64k flush=5m;# 5. 仅记录长会话(>10s)access_log /spool/logs/long-sessions.log basic if=$session_time\>10;server {listen 3306; # 例如:MySQL 代理proxy_pass backend:3306;}
}
七、测试与验证
-
重载配置
sudo nginx -t && sudo systemctl reload nginx
-
模拟 TCP 会话
使用openssl s_client
或nc
建立连接并发送少量数据:# 简单测试:连接后等待 2 秒再关闭 { echo -n ''; sleep 2; } | nc example.com 3306
-
查看日志
tail -n20 /spool/logs/nginx-access.log
应看到类似:
192.168.1.10 [12/May/2025:10:30:00 +0000] tcp 0 0 2.001
表示 IP、协议、状态、字节数与时长。
八、常见问题与排查
问题 | 排查思路 |
---|---|
日志不生成 | - 确认 access_log 是否在 stream 或 server 上下文中- 检查 NGINX error.log |
缓冲后长时间未写盘 | - 检查 flush 设置是否过大- 缓冲区是否未满 - 触发重载时会主动写盘 |
gzip 日志无法解压 | - 确认 NGINX 编译时已链接 zlib - 使用 zcat 或 gunzip -t 测试 |
带变量路径日志无缓存(频繁打开/关闭) | - open_log_file_cache 是否已配置- min_uses 、inactive 参数是否合理 |
九、最佳实践
- 预分配缓冲:根据日志量合理设置
buffer
,避免频繁写盘。 - 压缩归档:对历史日志使用 gzip,节省磁盘空间,并可随时通过
zcat
查看。 - 文件描述符缓存:当日志路径含变量时,务必开启
open_log_file_cache
,减少系统调用开销。 - 条件记录:对关键会话(如长连接、大流量)单独出日志,便于聚焦排查。
- 定期清理:通过外部脚本或 logrotate 管理日志文件,避免目录爆满。
十、总结
ngx_stream_log_module
为 NGINX Stream 模块提供了灵活、高性能的会话日志能力。
- 从 格式定义、日志输出、缓冲与压缩、到 文件描述符缓存,覆盖了日志记录的全生命周期。
- 通过 条件记录 与 压缩归档 等功能,兼顾了性能与可用性。
掌握本篇内容后,你即可在 TCP/UDP 代理、数据库中间件、微服务网关等各类场景中,轻松实现精准、可扩展的会话日志记录。祝部署顺利,运维轻松!