Linux 日志系统架构:从 syslog 到 rsyslog 与 systemd-journald

日志系统,说白了就是操作系统的“黑匣子”。

我刚入行那会儿,被线上问题搞得焦头烂额,靠 tail -f /var/log/messages 一把梭。后来才意识到,日志不只是排障工具,更是审计、合规、安全溯源的核心资产。今天我就带你看看 Linux 日志系统的底层骨架——syslog 协议、rsyslog 的进化,以及 systemd-journald 带来的新玩法。

核心知识地图:本章我们会拆解三个层次——协议标准、传统实现、现代替代。理解了它们的关系,你就能在任何 Linux 发行版上快速定位日志问题。
Linux 日志系统核心架构 应用程序 / 服务 (Apache, Nginx, MySQL...) 日志接口层 syslog() / sd_journal_print() rsyslogd 配置文件:/etc/rsyslog.conf 输出:/var/log/ 文本文件 systemd-journald 存储:/run/log/journal 查询:journalctl 可配置转发 文本日志文件 /var/log/messages 二进制日志 journalctl -xe 远程日志服务器 图例 实线:直接调用 虚线:可选数据同步

3.1 syslog 协议:日志界的“普通话”

syslog 不是一个软件,是一种协议(RFC 5424)。它定义了日志的格式、等级和设备分类。你可以把协议理解成“所有日志说同一种语言”。

每条 syslog 消息包含三个核心字段:设施(Facility)、严重级别(Severity)、消息体。

字段说明常见值举例
Facility标识消息来源kern(0)、user(1)、mail(2)、daemon(3)、auth(4)、local0~local7(16~23)
Severity紧急程度(0~7)emerg(0)、alert(1)、crit(2)、err(3)、warning(4)、notice(5)、info(6)、debug(7)
消息体实际文本内容“Connection refused by port 22”

你想想看,有了这个标准,任何程序只要调用 syslog() 函数,日志就能被统一收集、过滤、路由。我在项目中见过三台不同厂商的设备,一台 Linux 服务器、一台 Cisco 交换机、一台 Fortinet 防火墙,全都能把日志发到同一台 rsyslog 服务器——就是靠的这个协议。

小技巧:开发自己的程序时,尽量用 local0 ~ local7 作为自定义设施,避免跟系统设施冲突。分配规则在团队内部用文档约定好就行。

3.2 rsyslog:高可靠的日志瑞士军刀

rsyslog 是 syslog 协议的经典实现,也是大多数 RHEL/CentOS 7/8 的默认日志守护进程。它的设计原则:稳定、可扩展、模块化。

配置文件在 /etc/rsyslog.conf 和 /etc/rsyslog.d/ 目录下。语法其实很简单:

# 格式:facility.severity  action
kern.*                       /var/log/kern.log
*.info;mail.none;authpriv.none   /var/log/messages
local0.info                  @192.168.1.100:514   # 远程发送

这个语法我当初第一次看觉得挺“怪”,但用习惯了就会发现它特别灵活。你可以在 severity 字段里写 =info 精确匹配,或者 !err 排除某个级别。

我曾经在线上搞过一个配置,想把所有 SSH 登录失败的日志单独存一份:

# /etc/rsyslog.d/ssh-failures.conf
authpriv.crit                /var/log/ssh-auth-failures.log

配合 fail2ban 消费这个文件,效果很好。注意 authpriv 设施的 crit 级别通常对应登录失败——但不同发行版略有差异,用之前建议先 grep 一下已有日志确认关键词。

避坑指南:rsyslog 默认监听 UDP 514 端口,但 UDP 不保证送达。生产环境收远程日志时,建议同时开 TCP 514 并使用 imtcp 模块。我曾经因为 UDP 丢日志,排查了整整两天才发现是网络拥塞导致尾部日志丢失——从那以后,远程日志一律用 TCP。

3.3 systemd-journald:新时代的结构化日志

systemd 成为 Linux 的 init 系统后,顺带带来了 systemd-journald。它的核心理念:结构化、二进制、自带元数据。

为什么说它“结构化”?你感受一下传统 syslog 的一条日志:

Dec  5 10:20:30 myserver sshd[12345]: Failed password for root from 10.0.0.1 port 22 ssh2

这条日志需要靠正则表达式去提取时间、主机、进程、用户名、IP。而 journald 把每条日志变成了一组键值对:

_PID=12345
_SYSTEMD_UNIT=sshd.service
MESSAGE=Failed password for root from 10.0.0.1 port 22 ssh2
_PRIORITY=4
_BOOT_ID=abc123def456

再加上 journalctl 命令,查询效率远超 grep。比如查今天凌晨 3 点的 SSH 日志:

journalctl -u sshd.service --since "2025-12-06 03:00:00" --until "2025-12-06 03:10:00"

我个人习惯在排查故障时先用 journalctl -xe 看最近几屏,如果发现对应 _TRANSPORT=journal 的日志数量异常,再用过滤条件精准定位。

3.4 双轨协作:如何让 rsyslog 和 journald 和谐共存

很多新系统(CentOS 8、Ubuntu 20.04+)默认同时运行 rsyslog 和 journald。journald 收原始结构化日志,rsyslog 从 journald 读取后再做传统文件落地。配置方式:

# /etc/rsyslog.conf 或模块配置
module(load="imjournal" JournalStateFile="/var/lib/rsyslog/imjournal.state")
*.*     /var/log/syslog

这里要注意状态文件 imjournal.state 记录了 rsyslog 已经读到 journald 的哪个位置。如果状态文件损坏或丢失,重启 rsyslog 时会重复消费所有日志——我有一次在测试环境就遇到了这个坑,结果 /var/log/syslog 瞬间暴涨到几十 GB。解决办法是删掉状态文件并重启 rsyslog,但正式环境一定记得备份。

组件优势劣势
rsyslog文本直接可读,支持远程转发,配置灵活无法保留结构化字段,解析需正则
systemd-journald结构化查询,自带索引,按 boot 分级二进制文件体积大,默认无持久化(需创建 /var/log/journal)

我的推荐策略:journald 做近端实时查询,rsyslog 做持久化归档和远程转发。这样既享受了 journalctl 的便利,又保留了传统文本日志的兼容性。

关键配置检查项:
  • journald 持久化:mkdir -p /var/log/journal && systemctl restart systemd-journald
  • rsyslog 读取 journald:确保 imjournal 模块加载且状态文件存在
  • 日志轮转:/etc/logrotate.d/rsyslog 和 journalctl --vacuum-size=1G 双管齐下

3.5 一张表总结三者的核心区别

特性syslog 协议rsyslogsystemd-journald
本质协议标准守护进程 + 实现守护进程 + 存储引擎
日志格式纯文本纯文本二进制(键值对)
过滤能力极弱基于 facility/severity任意字段
性能高中高中(写放大因子高)
常见场景网络设备、嵌入式传统 Linux 服务器现代 systemd 系统

明白这些之后,你再看任何 Linux 发行版的日志配置,其实都是在“协议(syslog)- 实现(rsyslog)- 现代替代(journald)”这个三角里做组合。碰到不熟悉的系统,先看进程:ps aux | grep -E 'rsyslog|journald',再查配置文件,90% 的日志问题都能快速定位。

嗯,这一章的内容就这么多。下一环节是实操,我们直接上手配置一台两全其美的日志服务器。