本文要点
- Ubuntu 上日志是两段接力:应用写
/dev/log(已被 journald 接管,实际指向/run/systemd/journal/dev-log),journald 再通过/run/systemd/journal/syslog这个 socket 转给 rsyslog,最后由 rsyslog 按规则写进/var/log/* - 规则的核心语法只有
facility.severity 动作一行:auth,authpriv.* /var/log/auth.log就是「认证类日志,任意级别,写进 auth.log」;;拼接多个条件,none表示排除 & stop是截断开关:命中上一条规则的消息执行完动作后不再往下走。实测local0加规则前grep -c在 syslog 里是1,加规则后是0,日志被完整改道- 规则生效顺序 =
/etc/rsyslog.d/*.conf的文件名字典序(本机 20 → 21 → 48 → 49 → 50),想抢在默认规则前拦下日志,文件名编号就要更小 - 开远程接收端必须配
ruleset:把input收到的消息绑到独立规则集,否则「收进来的日志」会再次命中转发规则,自己和自己在网络上打转 - ⚠️ 改配置前先跑
rsyslogd -N1校验语法;本机的单元文件没有ExecReload,实测systemctl reload rsyslog报Job type reload is not applicable,只能restart - ⚠️ UDP(
@)无连接无重传,跨网络会丢;重要日志用 TCP(@@)。514 端口不要暴露在公网——UDP 源地址可以伪造,等于给人一个反射放大器
Linux rsyslog 日志服务:从 facility 规则分流到远程日志归集
很多人对 Linux 日志的认知停在两步:出事了 journalctl -u xxx 查一下,或者 tail -f /var/log/syslog 看实时输出。但中间那一层——日志是被谁决定写进哪个文件的——常常是个黑盒:为什么认证日志去了 auth.log、内核日志去了 kern.log、而其他所有东西挤在 syslog 里?为什么有些日志查得到 journal 却在 /var/log 里找不到?
答案在 rsyslog。它是那台按 facility(设施)和 severity(级别)把日志分拣到不同目的地的分拣机,也是把日志从一台机器送到另一台机器的那双手。本文在一台 Ubuntu 22.04 云服务器(KVM,1 核、内存 1.9G,内核 5.15.0-30-generic)上把它的常用能力跑了一遍:读默认路由表、写自定义规则做定向落盘、开一个 UDP 接收端把日志转出去。文中输出的时间戳、条数、监听状态都是真实执行结果。
先搞清谁在管日志:journald 与 rsyslog 的分工
「systemd 时代还需要 rsyslog 吗」是个常见疑问。实际这台机器上两者是接力关系,各管一段:
应用/服务 → /dev/log → journald → /run/systemd/journal/syslog → rsyslogd → /var/log/*
(已被 journald 接管) (ForwardToSyslog) (imuxsock)这条链路的每一环都能在机器上查证:
ls -l /dev/log
systemctl cat syslog.socket | grep ListenDatagram
grep ForwardToSyslog /etc/systemd/journald.conf
grep -n 'module(load="imuxsock")' /etc/rsyslog.conflrwxrwxrwx 1 root root 28 Sep 9 13:56 /dev/log -> /run/systemd/journal/dev-log
ListenDatagram=/run/systemd/journal/syslog
#ForwardToSyslog=yes
13:module(load="imuxsock") # provides support for local system logging也就是说:/dev/log 这个传统 syslog 接口现在归 journald 所有,应用照旧往里写,但数据先落到 journal;ForwardToSyslog=yes(注释掉即为默认值)让 journald 再把它转发到 syslog.socket 持有的 /run/systemd/journal/syslog;rsyslog 的 imuxsock 模块从那个 socket 读,按 /etc/rsyslog.d/*.conf 的规则落盘。
这个分工解释了两个日常现象:
journalctl查得到的,/var/log里不一定有——rsyslog 的规则可能压根没把它写进文件(比如很多服务日志默认只进 journal);- rsyslog 挂了,日志也不会彻底丢——journald 仍在采集,
journalctl照样能查(只是/var/log/*会停更)。
第一步:先看清现状,再动配置
不要上来就改配置。先看服务状态和默认路由表:
systemctl status rsyslog --no-pager | head -5
grep -vE '^\s*#|^\s*$' /etc/rsyslog.d/50-default.conf
TriggeredBy: ● syslog.socket 这一行是前面那条接力链的直接证据——rsyslog 是被 socket 拉起来的。
下面六行就是这台机器的全部默认路由,逐行读一遍,rsyslog 的核心语法也就掌握了:
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
kern.* -/var/log/kern.log
mail.* -/var/log/mail.log
mail.err /var/log/mail.err
*.emerg :omusrmsg:*auth,authpriv.*/kern.*/mail.*:设施,设施.级别是选择器。*在级别位置表示「所有级别」,多个设施用逗号并列。所以认证、内核、邮件各有各的文件。*.*;auth,authpriv.none:;用来并列/覆盖条件。前半句是「所有设施所有级别」,auth,authpriv.none表示「但从这两个设施里排除(none)」。一行就完成了「除认证外的全部日志进 syslog」——这是auth.log与syslog不重复记录的关键。-前缀(-/var/log/syslog):异步写。不等待fsync就返回,日志量大时能显著降低写入阻塞;代价是机器掉电时最后几条可能丢。auth.log没有-,属于同步写。*.emerg :omusrmsg:*:级别是emerg(最高级别)时,动作不是写文件而是:omusrmsg:*——给所有登录用户发消息。这就是紧急日志会突然出现在你终端上的原因。
selector 之外:属性过滤器与 & stop
除了 设施.级别,rsyslog 还有一类按内容匹配的写法,本机的另两个配置文件用的就是它:
grep -vE '^\s*#|^\s*$' /etc/rsyslog.d/20-ufw.conf /etc/rsyslog.d/21-cloudinit.conf/etc/rsyslog.d/20-ufw.conf::msg,contains,"[UFW " /var/log/ufw.log
/etc/rsyslog.d/21-cloudinit.conf::syslogtag, isequal, "[CLOUDINIT]" /var/log/cloud-init.log
/etc/rsyslog.d/21-cloudinit.conf:& stop:msg,contains,"[UFW ":只要消息正文里含[UFW就命中,不管它是什么设施(防火墙日志的设施并不固定,按内容抓才可靠);:syslogtag, isequal, "[CLOUDINIT]":按 tag 精确匹配;& stop:&代表「上一条规则」,stop是动作——命中并写完文件后,这条消息到此为止,不再参与后续任何规则。云初始化的日志因此不会再去污染syslog。
& stop 是整个 rsyslog 配置里最值得记住的一招:没有它,你的自定义规则只是「多写一份」;有了它,才是「改道」。
第二步:写一条规则,把 local0 单独落盘
local0~local7 是留给本地自定义应用使用的设施(logger -p local0.xxx 就能往里写),最适合做验证。规则文件放 /etc/rsyslog.d/,编号决定顺序——这里用 49-,排在 50-default.conf 前面:
logger -p local0.notice 'demo: 规则生效前'
sleep 1
grep -c 'demo: 规则生效前' /var/log/syslog
cat > /etc/rsyslog.d/49-demo.conf <<'CONF'
local0.* /var/log/demo-local0.log
& stop
CONF
systemctl restart rsyslog
logger -p local0.notice 'demo: 规则生效后'
sleep 1
grep -c 'demo: 规则生效后' /var/log/syslog
cat /var/log/demo-local0.log
输出里三行各是一句话:
1:规则生效前,local0的消息按默认规则*.*;auth,authpriv.none进了/var/log/syslog;0:规则生效后,同一条消息在syslog里一条都查不到——被& stop拦住了;- 最后一行:它完整地落在了新文件里,格式是
Sep 18 21:05:09 MFY001832765701 root: demo: 规则生效后(时间、主机名、tag、正文),说明主机名和 tag 都是 rsyslog 自动补的。
两条实操提醒:
sleep 1不是凑数:syslog用的是异步写(前面那个-前缀),logger返回时数据可能还没落盘,紧接着grep会偶发查不到;logger -p local0.notice的-p是设施.级别,也可以用-t myapp指定 tag,让日志在文件里更好认。
第三步:开一个 UDP 接收端,把日志转出去
多机环境下最常见的需求是「把几台机器的日志汇到一台」。接收端要显式加载 imudp 模块并开一个 input,同时用 ruleset 给它绑定一套独立规则:
cat > /etc/rsyslog.d/48-remote.conf <<'CONF'
module(load="imudp")
input(type="imudp" port="5140" address="127.0.0.1" ruleset="remote")
ruleset(name="remote") {
action(type="omfile" file="/var/log/demo-remote.log")
}
local0.* @127.0.0.1:5140
CONF
systemctl restart rsyslog
logger -p local0.notice 'demo: 远程归集测试'
sleep 2
cat /var/log/demo-remote.log
ss -lunp | grep 5140
这段演示把「发送端 + 接收端」压在了同一台机器上(127.0.0.1 收自己),但走的是真实的 UDP 网络路径,所以链路是完整的:
module(load="imudp")+input(type="imudp" ...):加载 UDP 输入模块并监听端口。默认配置文件里这两行是注释掉的(/etc/rsyslog.conf第 17~22 行),要用得自己开;address="127.0.0.1":只监听本机回环地址。这次演示刻意不监听0.0.0.0——514/5140 这类端口暴露到公网等于开放一个日志注入点(UDP 源地址可伪造),内网归集时才绑定内网网卡;ruleset="remote":把输入收到的消息交给名为remote的规则集处理,而这个规则集里只有「写文件」一个动作。这一步是防环的关键:如果不绑 ruleset,收进来的日志会继续走默认规则集,又命中local0.* @127.0.0.1:5140那条转发规则,自己把自己再发一遍,形成网络上打转的环路;local0.* @127.0.0.1:5140:@是 UDP 转发,@@是 TCP 转发。TCP 有连接和重传,跨公网或对日志完整性要求高时应该用@@;ss -lunp | grep 5140:证明rsyslogd真的在监听——排障时先看这一行,比翻日志快得多。
一篇日志同时走了两条路(本地落盘 + UDP 转发)也顺带说明了一件事:没有 & stop 的规则只是「多写一份」,不是「改道」。这正是前一步和这一步的区别。
改配置的安全姿势与几个坑
改之前先校验语法。 rsyslogd -N1 会把配置完整解析一遍然后退出,不启动服务:
rsyslogd -N1rsyslogd: version 8.2112.0, config validation run (level 1), master config /etc/rsyslog.conf
rsyslogd: End of config validation run. Bye.配置写错时它会直接指出行号。这一步不能省——rsyslog 起不来的时候,/var/log/* 会整体停更,排查起来比改配置本身麻烦得多。
这台机器上 systemctl reload 用不了。 实测:
systemctl reload rsyslog; echo "rc=$?"Failed to reload rsyslog.service: Job type reload is not applicable for unit rsyslog.service.
rc=3原因是 rsyslog.service 里没有定义 ExecReload(systemctl cat rsyslog.service | grep -c ExecReload 返回 0)。所以改完配置只能 systemctl restart rsyslog。重启是秒级的,且 journald 侧采集不受影响——万一服务起不来,还能用 journalctl 把日志捞回来(journalctl 系统日志:从启动日志查询到按服务与时间过滤排查)。
顺序就是文件名顺序。 /etc/rsyslog.conf 里那行 $IncludeConfig /etc/rsyslog.d/*.conf 展开时按字典序,所以本机是 20-ufw → 21-cloudinit → 48-remote → 49-demo → 50-default。想抢在默认规则前拦下某类日志,把文件编号改小即可;反过来,如果你的规则总是不生效,先检查是不是被更靠前的 & stop 截断了。
别同时开 imuxsock 和 imjournal。 本机 /etc/rsyslog.conf 只加载了 imuxsock(外加收内核日志的 imklog)。网上不少教程会让你再加载 imjournal 直接读 journal——两个输入同时启用时,同一批日志会被采集两次,落盘结果是每条日志重复一遍。选一条链路就够了。
留意 $RepeatedMsgReduction on。 这是本机默认开启的选项:短时间内重复出现的相同消息会被折叠成一行 last message repeated N times。它不是丢日志,但用脚本按行统计时会数不准,需要精确计数时可以关掉它。
日志文件本身会撑爆磁盘。 rsyslog 只管写,不管清理,轮转交给 Linux logrotate 日志轮转:从轮转策略到防止磁盘写满;真写满了,救火流程见 Linux 磁盘空间排查与清理:从 df 到 journald 的完整救火流程。
收尾:把演示配置撤干净。 加过的接收端会一直占着端口,测试完记得删除配置文件并重启:
rm -f /etc/rsyslog.d/48-remote.conf /etc/rsyslog.d/49-demo.conf
rm -f /var/log/demo-remote.log /var/log/demo-local0.log
systemctl restart rsyslog小结
把 rsyslog 的用法压成几条:
- 链路:应用 →
/dev/log(journald)→ForwardToSyslog→syslog.socket→ rsyslogimuxsock→/var/log/*;journalctl查的是前半段,/var/log是后半段的产物; - 规则:
设施.级别 动作是骨架,,并列设施、;拼接条件、none排除、-异步写; - 改道:想让某类日志「只去新地方」,规则末尾必须跟
& stop,否则只是多写一份; - 顺序:规则按文件名排序执行,编号决定谁先谁后;
- 归集:接收端
module(load="imudp")+input(... ruleset="xxx"),转发端@(UDP)或@@(TCP),并务必用ruleset把收到的日志关进独立规则集; - 安全:改配置先
rsyslogd -N1,本机只能restart不能reload,514 端口永远不要暴露到公网。
最后一句实话:rsyslog 的价值不在于「能把日志写下来」,而在于「能决定日志去哪」。默认配置能跑,是因为它替你把常见日志分了类;一旦你要做集中归集、按业务分流、或者把审计日志单独隔离出来(配合 auditd 安全审计),就绕不开它的规则语法。好在核心只有一行 selector 加一个 & stop。
评论 (0)
暂无评论,快来抢沙发吧!