告警通知发不出去需先排查 systemd-journald 与 rsyslog 冲突:journald 默认存二进制日志,rsyslog 需启用 imjournal 模块并加入 systemd-journal 组;logger 测试须指定 -p facility;rsyslog 需显式允许 local0-local7;service 要配置 standardoutput/standardError;避免 inotifywait + tail-f 组合导致重复或漏告。

告警通知发不出去?先查 systemd-journald 和 rsyslog 是否冲突
linux 告警链路常卡在日志采集层——systemd-journald 默认把日志存在二进制文件里,而很多告警工具(比如 monit、自定义脚本)只读 /var/log/messages 这类文本日志。如果同时开了 rsyslog 又没配好转发,journald 会静默丢弃部分日志。
-
rsyslog启动后默认不自动拉取journald日志,需确认/etc/rsyslog.conf里有$IncludeConfig /etc/rsyslog.d/*.conf,且/etc/rsyslog.d/10-journal.conf存在并启用imjournal模块 - 检查
journalctl --no-pager -u rsyslog是否报imjournal: journal is not accessible,这是权限问题,要把rsyslog加入systemd-journal用户组:usermod -a -G systemd-journal syslog - 别直接改
/etc/rsyslog.conf的$ModLoad imjournal行——有些发行版(如 centos 8+)要求用module(load="imjournal")语法,旧写法会导致模块加载失败,但rsyslog不报错,只静默跳过
用 logger 测试告警通路时,为什么加了 -t 却收不到?
logger 是最轻量的告警触发方式,但标签(-t)和 facility 配合不当,会被过滤掉。很多 rsyslog 规则靠 programname 或 syslogtag 匹配,不是所有规则都认 -t 的值。
- 测试命令别只写
logger -t myalert "disk full",先加-p local0.info显式指定 facility:logger -p local0.info -t myalert "disk full" - 对应 rsyslog 规则要写成
if $programname == 'myalert' and $syslogfacility-text == 'local0' then /var/log/myalert.log,注意$programname取的是-t值,但仅当-p指定的 facility 被 rsyslog 允许接收时才生效 - CentOS/RHEL 默认 rsyslog 配置会丢弃
local0-local7的日志,除非显式放开:$SystemMaxFileSize 100M下面加一行$AllowedSender udp 127.0.0.1不管用,得在/etc/rsyslog.conf开头加$ModLoad imudp和$UDPServerRun 514才行——但这其实是走 UDP 路径,和本地logger无关;真正要改的是/etc/rsyslog.d/50-default.conf里的*.*;auth,authpriv.none这行,把local0.*加进去
systemd 服务挂了,怎么让告警带出 exit code 和 stdout?
单纯靠 systemctl is-failed myapp.service 只能知道“失败”,但不知道是段错误还是配置写错。想拿到完整上下文,必须绕过 journalctl 的默认截断和缓冲逻辑。
- 在 service 文件里加
StandardOutput=journal+console和StandardError=journal+console,否则journalctl -u myapp --no-pager -n 100可能拿不到最后几行输出 - exit code 要用
systemctl show --Property=ExecMainStatus myapp.service,不是systemctl status输出里的 “code=exited, status=1”——后者可能被 systemd 缓存,而ExecMainStatus是实时读 cgroup 的 - 写告警脚本时别用
systemctl status myapp | grep "failed"判断状态,这容易误判(比如服务刚启动中,status 输出含 “activating” 但还没 failed)。正确做法是:systemctl is-active --quiet myapp && systemctl is-failed --quiet myapp,两个都返回非零才真失败
告警重复发送?大概率是 inotifywait + tail -F 组合翻车
用文件监控做告警(比如监听 /var/log/nginx/error.log)时,inotifywait 触发太快,tail -F 还没 flush,导致同一条日志被反复读;或者 logrotate 一转,tail -F 自动跟新文件,但 inotifywait 还在盯旧 inode,漏告警。
- 放弃
inotifywait+tail组合,改用journalctl -u nginx --since "1 hour ago" --no-pager | grep "error"定时轮询——虽然低效,但稳定。如果非要实时,用systemd-cat把 nginx 日志打到 journal,再用journalctl -o json-sse -u nginx流式消费 - 如果坚持用文件监控,
inotifywait -m -e move_self,create /var/log/nginx/比监听modify更可靠:logrotate 做rename时会触发move_self,这时 reloadtail进程 -
tail -F在 NFS 或某些容器挂载路径下行为异常,换成tail -n 0 -F并加--pid=$$配合信号捕获,收到SIGHUP时手动kill $(cat /tmp/tail.pid)再重启,避免僵尸 tail 占着文件句柄
事情说清了就结束。真正难的不是写通知逻辑,是厘清日志从进程 stdout 到落盘、再到被采集、最后匹配规则的每一跳权限和缓冲策略——中间任何一环默认值变了,告警就哑火。