基于:glibc、systemd journald、rsyslog及MAN文档
适用系统:Ubuntu
1. 总体架构
┌─────────────┐
│ Application │ syslog(3) 调用
└──────┬──────┘
│ write() to /dev/log
▼
┌─────────────────────────────────────┐
│ /dev/log → /run/systemd/journal/ │ ← systemd-journald-dev-log.socket
│ dev-log │ (journald 拥有此 socket)
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ systemd-journald │ ← 结构化存储到 journal 文件
│ (ForwardToSyslog=yes) │ /var/log/journal/ 或 /run/log/journal/
└──────────────┬──────────────────────┘
│ 转发到 /run/systemd/journal/syslog
▼
┌─────────────────────────────────────┐
│ syslog.socket → rsyslogd │ ← rsyslog 的 imuxsock 模块读取
│ (imuxsock) │
└──────────────┬──────────────────────┘
│ 根据 /etc/rsyslog.conf 规则
▼
┌─────────────────────────────────────┐
│ /var/log/syslog │ ← 传统文本日志文件
│ /var/log/auth.log │
│ /var/log/kern.log │
└─────────────────────────────────────┘
2. 逐层深度解析
2.1 应用层:syslog(3) 函数
函数原型
c
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
调用流程(glibc 源码分析)
glibc 中 syslog() 的实现位于 misc/syslog.c。核心逻辑:
- 首次调用时初始化 :如果尚未
openlog(),自动调用openlog(NULL, 0, LOG_USER) - 构造消息 :格式化时间戳、ident(程序名)、PID(如果设置了
LOG_PID) - 打开
/dev/logsocket :socket(AF_UNIX, SOCK_DGRAM, 0)→connect()到/dev/log - 发送消息 :
sendto()或write()发送格式化后的 syslog 协议消息
关键源码片段逻辑(伪代码):
c
void __vsyslog_chk(int pri, int flag, const char *fmt, va_list ap) {
// 1. 构造消息头: "<PRI>timestamp hostname program[pid]: "
// 2. 格式化用户消息
// 3. 打开 /dev/log (AF_UNIX, SOCK_DGRAM)
// 4. sendto(fd, buf, len, 0, &syslog_addr, addr_len)
// 5. 如果失败且设置了 LOG_CONS,尝试写控制台
}
发送的消息格式(传统 BSD syslog 协议):
<PRI>timestamp hostname program[pid]: message
其中 PRI = facility * 8 + level。例如 LOG_USER | LOG_INFO → PRI = (1<<3) | 6 = 14,消息以 <14> 开头。
重要难点:为什么是 /dev/log?
/dev/log 是一个 Unix Domain Socket (AF_UNIX, SOCK_DGRAM),传统上由 syslogd/rsyslogd 创建。在 systemd 系统中:
/dev/log → /run/systemd/journal/dev-log
这个 socket 由 systemd-journald-dev-log.socket 单元创建并归 journald 所有。
2.2 传输层:/dev/log → journald
systemd-journald-dev-log.socket 单元
ini
[Socket]
ListenDatagram=/run/systemd/journal/dev-log
PassCredentials=yes
PassSecurity=yes
Service=systemd-journald.service
SocketMode=0666
Symlinks=/dev/log
ReceiveBuffer=8M
SendBuffer=8M
关键点:
ListenDatagram:创建一个 DGRAM 类型 的 Unix socketSymlinks=/dev/log:自动创建/dev/log → /run/systemd/journal/dev-log符号链接PassCredentials=yes:接收SCM_CREDENTIALS辅助数据,获取发送进程的 PID、UID、GIDSocketMode=0666:任何人都可以写入
journald 接收处理
当 journald 收到消息后:
- 解析 syslog 协议:提取 PRI(facility + level)、时间戳、主机名、程序名
- 注入元数据 :自动添加
_PID=,_UID=,_GID=,_COMM=,_EXE=,_SYSTEMD_CGROUP=等字段 - 速率限制 :默认
RateLimitIntervalSec=30s,RateLimitBurst=10000 - 存储 :写入 journal 文件(
/var/log/journal/<machine-id>/或/run/log/journal/)
重要难点:SCM_CREDENTIALS
这是 Unix socket 的 辅助数据(ancillary data) 机制。当发送方通过 Unix socket 发送消息时,内核会自动附加发送进程的真实凭据(PID、UID、GID),发送方无法伪造。这使得 journald 能可靠地知道谁发送了日志,即使发送方声称自己是其他程序。
c
// 发送方示例(由 glibc syslog() 内部完成)
struct msghdr msg = {0};
struct iovec iov;
char buf[4096];
union {
struct cmsghdr cmsg;
char control[CMSG_SPACE(sizeof(struct ucred))];
} control;
// 填充消息...
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control.control;
msg.msg_controllen = sizeof(control.control);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDENTIALS;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
cred->pid = getpid(); // 内核会验证并覆盖
cred->uid = getuid(); // 内核会验证并覆盖
cred->gid = getgid(); // 内核会验证并覆盖
sendmsg(fd, &msg, 0);
2.3 转发层:journald → rsyslog
journald.conf 配置
ini
# /usr/lib/systemd/journald.conf.d/syslog.conf
[Journal]
ForwardToSyslog=yes
当 ForwardToSyslog=yes 时,journald 在收到每条日志后,会将其转发 到 /run/systemd/journal/syslog socket。
syslog.socket 单元
ini
[Socket]
ListenDatagram=/run/systemd/journal/syslog
SocketMode=0666
PassCredentials=yes
PassSecurity=yes
ReceiveBuffer=8M
这个 socket 用于激活 rsyslog 服务。当 journald 向此 socket 写入消息时,systemd 会启动 rsyslog.service(如果尚未运行)。
重要难点:双 socket 架构
/dev/log (journald-dev-log.socket) ← 应用写入这里
↓ journald 接收并处理
↓ ForwardToSyslog=yes
/run/systemd/journal/syslog (syslog.socket) ← rsyslog 从这里读取
为什么需要两个 socket?
dev-logsocket:接收原始 syslog 消息,由 journald 直接管理syslogsocket:用于 journald 向传统 syslog 守护进程转发消息
这样 journald 可以先处理消息(注入元数据、速率限制、结构化存储),再将处理后的消息转发给 rsyslog 做传统文件输出。
2.4 输出层:rsyslog → /var/log/syslog
rsyslog 配置
nginx
# /etc/rsyslog.conf
module(load="imuxsock") # 读取 Unix socket 输入
# /etc/rsyslog.d/50-default.conf
*.*;auth,authpriv.none -/var/log/syslog
auth,authpriv.* /var/log/auth.log
kern.* -/var/log/kern.log
imuxsock 模块
imuxsock(Input Module Unix Socket)是 rsyslog 的输入模块,默认监听 /dev/log。但在 systemd 系统中:
/dev/log已被 journald 占用- rsyslog 改为从
/run/systemd/journal/syslog读取(通过syslog.socket激活)
imuxsock 的工作流程:
- 打开
/run/systemd/journal/syslogsocket(或传统/dev/log) - 接收 syslog 协议消息
- 解析 facility 和 level
- 匹配规则(selector + action)
- 写入对应的日志文件
规则解析
以 *.*;auth,authpriv.none -/var/log/syslog 为例:
| 部分 | 含义 |
|---|---|
*.* |
所有 facility 的所有 level |
;auth,authpriv.none |
排除 auth 和 authpriv facility |
-/var/log/syslog |
写入文件,- 表示每次写后不同步(提高性能) |
3. 完整时序图
/var/log/syslog rsyslogd (imuxsock) /run/systemd/journal/syslog Journal File systemd-journald /dev/log (socket) glibc syslog() Application /var/log/syslog rsyslogd (imuxsock) /run/systemd/journal/syslog Journal File systemd-journald /dev/log (socket) glibc syslog() Application #mermaid-svg-HtC0qRgZN8YOrrfQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HtC0qRgZN8YOrrfQ .error-icon{fill:#552222;}#mermaid-svg-HtC0qRgZN8YOrrfQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HtC0qRgZN8YOrrfQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HtC0qRgZN8YOrrfQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HtC0qRgZN8YOrrfQ .marker.cross{stroke:#333333;}#mermaid-svg-HtC0qRgZN8YOrrfQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HtC0qRgZN8YOrrfQ p{margin:0;}#mermaid-svg-HtC0qRgZN8YOrrfQ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HtC0qRgZN8YOrrfQ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-HtC0qRgZN8YOrrfQ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HtC0qRgZN8YOrrfQ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-HtC0qRgZN8YOrrfQ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-HtC0qRgZN8YOrrfQ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-HtC0qRgZN8YOrrfQ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-HtC0qRgZN8YOrrfQ .sequenceNumber{fill:white;}#mermaid-svg-HtC0qRgZN8YOrrfQ #sequencenumber{fill:#333;}#mermaid-svg-HtC0qRgZN8YOrrfQ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-HtC0qRgZN8YOrrfQ .messageText{fill:#333;stroke:none;}#mermaid-svg-HtC0qRgZN8YOrrfQ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HtC0qRgZN8YOrrfQ .labelText,#mermaid-svg-HtC0qRgZN8YOrrfQ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-HtC0qRgZN8YOrrfQ .loopText,#mermaid-svg-HtC0qRgZN8YOrrfQ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-HtC0qRgZN8YOrrfQ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HtC0qRgZN8YOrrfQ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-HtC0qRgZN8YOrrfQ .noteText,#mermaid-svg-HtC0qRgZN8YOrrfQ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-HtC0qRgZN8YOrrfQ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HtC0qRgZN8YOrrfQ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HtC0qRgZN8YOrrfQ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HtC0qRgZN8YOrrfQ .actorPopupMenu{position:absolute;}#mermaid-svg-HtC0qRgZN8YOrrfQ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-HtC0qRgZN8YOrrfQ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HtC0qRgZN8YOrrfQ .actor-man circle,#mermaid-svg-HtC0qRgZN8YOrrfQ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-HtC0qRgZN8YOrrfQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 格式化消息\n<14> ... hello world /dev/log ->> /run/systemd/journal/dev-log 1.解析 syslog PRI\n2.注入元数据(_PID,_UID)\n3.速率限制检查 1.解析 facility/level\n2.匹配规则\n3.格式化输出 altForwardToSyslog=yes syslog(LOG_INFO, "hello world")sendto() DGRAM接收消息 + SCM_CREDENTIALS写入结构化 journal转发 syslog 消息imuxsock 接收写入 /var/log/syslog
4. 实践 Demo
4.1 基础 Demo:C 程序调用 syslog()
c
// demo_syslog.c
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
int main() {
// 打开日志连接
openlog("mydemo", LOG_PID | LOG_CONS, LOG_USER);
printf("PID: %d\n", getpid());
printf("正在发送日志到 syslog...\n");
// 发送不同级别的日志
syslog(LOG_EMERG, "=== EMERG: 系统不可用 ===");
syslog(LOG_ALERT, "=== ALERT: 必须立即处理 ===");
syslog(LOG_CRIT, "=== CRIT: 严重条件 ===");
syslog(LOG_ERR, "=== ERR: 错误条件 ===");
syslog(LOG_WARNING, "=== WARNING: 警告 ===");
syslog(LOG_NOTICE, "=== NOTICE: 正常但重要 ===");
syslog(LOG_INFO, "=== INFO: 信息消息 ===");
syslog(LOG_DEBUG, "=== DEBUG: 调试消息 ===");
// 使用 %m 打印 errno
FILE *f = fopen("/nonexistent/file", "r");
if (!f) {
syslog(LOG_ERR, "打开文件失败: %m");
}
closelog();
printf("完成!请查看以下位置:\n");
printf(" 1. journalctl -f (实时查看 journal)\n");
printf(" 2. tail -f /var/log/syslog (查看传统日志)\n");
printf(" 3. journalctl _PID=%d (过滤此进程)\n", getpid());
return 0;
}
编译运行:
bash
gcc -o demo_syslog demo_syslog.c
./demo_syslog
4.2 实时观察流水线
终端 1:实时观察 journal
bash
journalctl -f -n 0
终端 2:实时观察 syslog 文件
bash
tail -f /var/log/syslog
终端 3:运行 demo
bash
./demo_syslog
你会看到:
- journalctl 终端几乎立即显示日志(毫秒级)
- syslog 终端也很快显示(journald 转发 + rsyslog 处理)
4.3 验证流水线各环节
bash
# 1. 验证 /dev/log 指向 journald
ls -la /dev/log
# 输出: lrwxrwxrwx ... /dev/log -> /run/systemd/journal/dev-log
# 2. 验证 journald 拥有 dev-log socket
ss -xlp | grep dev-log
# 输出: ... /run/systemd/journal/dev-log ... users:(("systemd-journal",...))
# 3. 验证 syslog socket
ss -xlp | grep syslog
# 输出: ... /run/systemd/journal/syslog ...
# 4. 查看 journald 配置
systemd-analyze cat-config systemd/journald.conf | grep ForwardToSyslog
# 输出: ForwardToSyslog=yes
# 5. 查看 rsyslog 配置
cat /etc/rsyslog.d/50-default.conf | head -10
# 6. 查看特定进程的 journal 日志
./demo_syslog # 记下 PID
journalctl _PID=<PID> -o verbose # 查看完整元数据
4.4 高级 Demo:观察 SCM_CREDENTIALS
c
// demo_cred.c - 尝试伪造身份
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
int main() {
// 即使我们声称是 "root",journald 通过 SCM_CREDENTIALS
// 知道真实的 PID/UID/GID
openlog("fake-root", LOG_PID, LOG_AUTH);
syslog(LOG_INFO, "我是谁?journald 知道真相");
closelog();
printf("查看 journald 记录的元数据:\n");
printf("journalctl _PID=%d -o verbose --no-pager\n", getpid());
return 0;
}
运行后查看:
bash
journalctl _PID=<PID> -o verbose --no-pager
你会看到类似输出:
_PID=12345
_UID=1000 ← 真实 UID,不是 0
_GID=1000
_COMM=demo_cred
_EXE=/home/user/demo_cred
SYSLOG_IDENTIFIER=fake-root
注意 _UID=1000 而不是 0------这是 SCM_CREDENTIALS 的作用,内核强制附加真实凭据,程序无法伪造。
4.5 调试:strace 观察系统调用
bash
# 跟踪 syslog() 的系统调用
strace -e trace=network,socket,write -f ./demo_syslog 2>&1 | grep -E 'socket|connect|sendto|write'
典型输出:
socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/dev/log"}, 110) = 0
sendto(3, "<14>Jun 12 14:30:00 mydemo[12345]: === INFO: 信息消息 ===", 60, 0, NULL, 0) = 60
5. 重要难点专题
5.1 难点一:为什么 /dev/log 只能被一个进程绑定?
Unix Domain Socket (DGRAM 类型) 的绑定规则:
- 一个 socket 文件路径只能被一个进程
bind() - 但 多个进程可以同时打开同一个 DGRAM socket 进行读写
- 关键在于:谁 bind 了路径 决定了谁接收消息
在 systemd 系统中:
systemd-journald-dev-log.socket在 systemd 初始化时创建并 bind/run/systemd/journal/dev-log/dev/log作为符号链接指向它- 只有 journald 从该 socket 接收消息
- rsyslog 不能直接读取
/dev/log,必须通过 journald 转发
5.2 难点二:ForwardToSyslog 的默认值
| 配置来源 | ForwardToSyslog 值 |
|---|---|
| 编译默认值 | no |
/etc/systemd/journald.conf |
注释掉,使用默认值 no |
/usr/lib/systemd/journald.conf.d/syslog.conf |
yes(发行版覆盖) |
| 有效值 | yes |
这意味着:
- 编译时默认不转发到 syslog
- 但 Ubuntu/Debian 通过 drop-in 文件覆盖为
yes - 这是为了兼容传统 syslog 工具链
5.3 难点三:rsyslog 的 imuxsock 到底从哪里读取?
传统上,imuxsock 从 /dev/log 读取。但在 systemd 系统中:
/dev/log被 journald 占用- rsyslog 通过
syslog.socket被激活 syslog.socket监听/run/systemd/journal/syslog- journald 在
ForwardToSyslog=yes时向此 socket 写入 - rsyslog 的
imuxsock模块从syslog.socket继承的 fd 读取
实际上,imuxsock 也可以配置为读取其他 socket 路径:
nginx
# 如果 rsyslog 需要直接从 /dev/log 读取(不通过 journald 转发)
module(load="imuxsock" SysSock.Use="off")
# 然后配置其他 socket
5.4 难点四:消息格式转换
| 阶段 | 消息格式 |
|---|---|
| syslog() 发送 | <PRI>timestamp hostname prog[pid]: msg |
| journald 接收 | 解析 PRI → facility + level,存储为结构化字段 |
| journald 转发 | 重新格式化为 syslog 协议格式 |
| rsyslog 接收 | 解析 facility/level,匹配规则 |
| rsyslog 写入 | 根据模板格式化(默认:%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%) |
5.5 难点五:速率限制与消息丢失
ini
# journald.conf
RateLimitIntervalSec=30s
RateLimitBurst=10000
- 在 30 秒窗口内,如果某个 service 发送超过 10000 条消息,后续消息会被丢弃
- 被丢弃的消息会有一条替代消息:"MESSAGE_ID=... Suppressed N messages from /user.slice/..."
- 这保护系统不被日志洪泛打垮
6. 完整架构图
#mermaid-svg-sKA5rwDR4qWY5OLG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sKA5rwDR4qWY5OLG .error-icon{fill:#552222;}#mermaid-svg-sKA5rwDR4qWY5OLG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sKA5rwDR4qWY5OLG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sKA5rwDR4qWY5OLG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sKA5rwDR4qWY5OLG .marker.cross{stroke:#333333;}#mermaid-svg-sKA5rwDR4qWY5OLG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sKA5rwDR4qWY5OLG p{margin:0;}#mermaid-svg-sKA5rwDR4qWY5OLG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sKA5rwDR4qWY5OLG .cluster-label text{fill:#333;}#mermaid-svg-sKA5rwDR4qWY5OLG .cluster-label span{color:#333;}#mermaid-svg-sKA5rwDR4qWY5OLG .cluster-label span p{background-color:transparent;}#mermaid-svg-sKA5rwDR4qWY5OLG .label text,#mermaid-svg-sKA5rwDR4qWY5OLG span{fill:#333;color:#333;}#mermaid-svg-sKA5rwDR4qWY5OLG .node rect,#mermaid-svg-sKA5rwDR4qWY5OLG .node circle,#mermaid-svg-sKA5rwDR4qWY5OLG .node ellipse,#mermaid-svg-sKA5rwDR4qWY5OLG .node polygon,#mermaid-svg-sKA5rwDR4qWY5OLG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sKA5rwDR4qWY5OLG .rough-node .label text,#mermaid-svg-sKA5rwDR4qWY5OLG .node .label text,#mermaid-svg-sKA5rwDR4qWY5OLG .image-shape .label,#mermaid-svg-sKA5rwDR4qWY5OLG .icon-shape .label{text-anchor:middle;}#mermaid-svg-sKA5rwDR4qWY5OLG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sKA5rwDR4qWY5OLG .rough-node .label,#mermaid-svg-sKA5rwDR4qWY5OLG .node .label,#mermaid-svg-sKA5rwDR4qWY5OLG .image-shape .label,#mermaid-svg-sKA5rwDR4qWY5OLG .icon-shape .label{text-align:center;}#mermaid-svg-sKA5rwDR4qWY5OLG .node.clickable{cursor:pointer;}#mermaid-svg-sKA5rwDR4qWY5OLG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sKA5rwDR4qWY5OLG .arrowheadPath{fill:#333333;}#mermaid-svg-sKA5rwDR4qWY5OLG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sKA5rwDR4qWY5OLG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sKA5rwDR4qWY5OLG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sKA5rwDR4qWY5OLG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sKA5rwDR4qWY5OLG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sKA5rwDR4qWY5OLG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sKA5rwDR4qWY5OLG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sKA5rwDR4qWY5OLG .cluster text{fill:#333;}#mermaid-svg-sKA5rwDR4qWY5OLG .cluster span{color:#333;}#mermaid-svg-sKA5rwDR4qWY5OLG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sKA5rwDR4qWY5OLG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sKA5rwDR4qWY5OLG rect.text{fill:none;stroke-width:0;}#mermaid-svg-sKA5rwDR4qWY5OLG .icon-shape,#mermaid-svg-sKA5rwDR4qWY5OLG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sKA5rwDR4qWY5OLG .icon-shape p,#mermaid-svg-sKA5rwDR4qWY5OLG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sKA5rwDR4qWY5OLG .icon-shape .label rect,#mermaid-svg-sKA5rwDR4qWY5OLG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sKA5rwDR4qWY5OLG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sKA5rwDR4qWY5OLG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sKA5rwDR4qWY5OLG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Application
syslog()
stdout/stderr
sd_journal_print()
/dev/log
systemd-journald
Journal Files
/run/systemd/journal/syslog
rsyslogd
/var/log/syslog
/var/log/auth.log
/var/log/kern.log
7. 关键命令速查
bash
# 查看 journald 状态
systemctl status systemd-journald
# 查看 journald 配置
systemd-analyze cat-config systemd/journald.conf
# 查看 rsyslog 状态
systemctl status rsyslog
# 查看 rsyslog 配置
cat /etc/rsyslog.conf
cat /etc/rsyslog.d/50-default.conf
# 实时查看 journal
journalctl -f
# 查看特定 PID 的日志
journalctl _PID=<PID>
# 查看特定程序的日志
journalctl SYSLOG_IDENTIFIER=mydemo
# 查看完整元数据
journalctl -o verbose _PID=<PID>
# 查看 socket 信息
ss -xlp | grep -E 'log|syslog'
# 查看文件链接
ls -la /dev/log
readlink -f /dev/log
# 测试 syslog 发送
logger "Hello from logger command"
echo "test" | systemd-cat -t mytag
# 查看 journal 文件位置
ls -la /var/log/journal/
8. 总结
| 层级 | 组件 | 职责 | 关键机制 |
|---|---|---|---|
| 应用 | syslog(3) |
格式化并发送日志 | sendto() DGRAM 到 /dev/log |
| 传输 | /dev/log |
Unix socket 传输 | symlink → journald dev-log socket |
| 采集 | systemd-journald |
接收、解析、存储、转发 | SCM_CREDENTIALS, 结构化存储 |
| 转发 | ForwardToSyslog=yes |
转发到传统 syslog | 写入 /run/systemd/journal/syslog |
| 输出 | rsyslogd |
规则匹配、文件写入 | imuxsock, selector/action 规则 |
| 持久化 | /var/log/syslog |
传统文本日志 | rsyslog 模板格式化输出 |
核心设计思想 :journald 作为中央日志采集器 ,负责结构化存储和元数据注入;rsyslog 作为传统输出引擎,负责按规则写入文本文件。两者通过 Unix socket 桥接,各司其职。