记一次 rsyslog 模板配置错误导致日志重复写入与磁盘异常增长的排查记录

rsyslog 模板配置中 action(type="omfile") 与全局 $template 重复生效,导致同一条日志被写入两次,引发磁盘空间异常增长与 journald 回压。

问题背景

生产环境一台核心日志服务器(CentOS 7 + rsyslog 8.24)在 2026-09-18 凌晨开始出现磁盘空间告警,/var/log 分区在 4 小时内从 38% 涨到 87%。该服务器承担全网 200+ 台设备(交换机、防火墙、Linux 服务器、Windows 事件转发)的集中日志收集,日常写入量约 1.2GB/天。监控显示 /var/log/messages 单文件在 3 小时内从 180MB 暴增到 2.1GB,且持续高速写入。

故障现象

  1. 磁盘空间异常df -h 显示 /var/log 持续增长,du -sh /var/log/messagesls -lh 显示文件大小每分钟增加 8-12MB。
  2. 日志内容重复tail -f /var/log/messages 发现同一条 SSH 登录事件、cron 执行记录每隔 1-2 秒出现两次,时间戳完全一致。
  3. journald 回压systemctl status systemd-journald 显示 Storage=autoRuntimeJournal 持续增长,journalctl --disk-usage 报告 4.2GB,且 journalctl -f 输出明显滞后。
  4. 无明显错误日志:rsyslog 本身未报错,/var/log/rsyslog.log 仅记录正常启动与 HUP 重载。

排查过程

第一步:定位写入源

1
lsof +L1 | grep /var/log/messages

发现两个 rsyslog 进程(PID 1247、1248)同时持有该文件句柄,且两个进程的打开文件描述符指向同一 inode。这表明 rsyslog 内部存在两个独立的写入动作。

第二步:检查 rsyslog 配置

1
rsyslogd -N1 -f /etc/rsyslog.conf

未报错。进一步检查 /etc/rsyslog.d/ 下的自定义配置:

1
cat /etc/rsyslog.d/99-custom.conf

关键片段:

1
2
3
4
5
6
7
8
9
$template CustomMsgFormat,"%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%\n"

if $syslogfacility-text == 'authpriv' or $syslogfacility-text == 'cron' then {
    action(type="omfile"
           file="/var/log/messages"
           template="CustomMsgFormat")
}

*.info;mail.none;authpriv.none;cron.none   /var/log/messages

这里同时存在两种写入方式:

  1. 显式的 action(type="omfile") 模块调用,指定了 template
  2. 传统的 selector + file 语法 *.info;... /var/log/messages

传统语法会隐式使用全局 $template(或默认 RSYSLOG_TraditionalFileFormat),而 action 模块又显式指定了另一个模板。两者同时生效,导致同一条日志被写入两次

第三步:验证重复写入根因

1
grep -n "CustomMsgFormat" /etc/rsyslog.conf /etc/rsyslog.d/*.conf

确认全局 $template CustomMsgFormat 定义在 /etc/rsyslog.conf 第 87 行,而 99-custom.conf 里又定义了同名模板并在 action 中引用。rsyslogd 解析时,后加载的 99-custom.conf 覆盖了全局模板,但传统 selector 行仍使用默认模板,两者并行写入。

第四步:确认磁盘增长与 journald 关系

1
journalctl --since "2026-09-18 02:00:00" | head -20

发现 journald 本身也在记录大量重复的 rsyslog 启动/重载事件(因为 rsyslog 不断触发 HUP),形成正反馈循环:日志重复写入 → journald 刷盘 → 触发 rsyslog 自身日志 → 再次重复。

解决方案

立即止血(热修复)

1
2
3
4
5
6
7
8
9
# 停止 rsyslog,避免继续写入
systemctl stop rsyslog

# 备份并清空异常增长的日志文件(保留 inode)
cp /var/log/messages /var/log/messages.20260918.bak
: > /var/log/messages

# 清理 journald 运行时日志
journalctl --vacuum-time=2d

根治配置

编辑 /etc/rsyslog.d/99-custom.conf删除传统 selector 行,只保留 action 模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$template CustomMsgFormat,"%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%\n"

if $syslogfacility-text == 'authpriv' or $syslogfacility-text == 'cron' then {
    action(type="omfile"
           file="/var/log/messages"
           template="CustomMsgFormat"
           flushInterval="10"
           flushOnTXEnd="on")
}

# 其他 facility 仍走传统 selector,但明确指定模板
*.info;mail.none;authpriv.none;cron.none   action(type="omfile" file="/var/log/messages" template="RSYSLOG_TraditionalFileFormat")

同时在 /etc/rsyslog.conf 全局段添加:

1
2
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$template RSYSLOG_TraditionalFileFormat,"%TIMESTAMP% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%\n"

重载验证

1
2
3
rsyslogd -N1 -f /etc/rsyslog.conf
systemctl restart rsyslog
systemctl status rsyslog

检查 tail -f /var/log/messages,确认同一事件不再重复出现。

根因分析

rsyslog 支持两种配置语法共存:

  • 传统 selector + 文件路径:隐式使用全局 $template 或默认 RSYSLOG_TraditionalFileFormat
  • action(type=“omfile”) 模块:显式指定 template 参数

当两者同时指向同一文件,且模板名称或内容不同时,rsyslog 会为每个匹配的 rule 创建独立的输出动作,导致日志被多次写入。本次事故的直接诱因是「自定义 action 模块与传统 selector 同时存在,且未统一模板」

预防措施

  1. 配置规范化:全站 rsyslog 配置统一改用 action(type="omfile") 模块语法,彻底弃用传统 selector + 文件路径写法。
  2. 模板集中管理:所有自定义模板放在 /etc/rsyslog.d/00-templates.conf,并在 rsyslog.conf 顶部 include;各业务配置仅引用模板名,不重复定义。
  3. 配置审查脚本:在 CI/CD 流水线中加入 rsyslog 配置 lint(检查是否同时存在传统 selector 与 action 模块指向同一文件)。
  4. 监控增强
    • 新增 rsyslog 进程打开文件数监控(lsof | grep rsyslog | wc -l
    • /var/log 目录 inode 使用率监控(df -i
    • journald 磁盘使用率告警阈值从 80% 提前到 60%
  5. 日志轮转策略收紧logrotate 配置中增加 size 100M 触发条件,配合 copytruncate 避免 rsyslog 句柄失效。

总结

一次看似「日志模板自定义」的细微配置差异,在 rsyslog 双语法共存的场景下演变为磁盘空间危机与 journald 回压。本次排查再次印证:日志系统配置必须「单一事实来源」,传统 selector 与 action 模块不可混用指向同一目标文件。后续将把 rsyslog 配置统一收敛到模块化语法,并纳入变更前 lint 门禁,避免同类问题复发。

延伸阅读

使用 Hugo 构建
主题 StackJimmy 设计