3、journald系统日志:systemd-journald架构、journalctl命令详解、持久化日志配置
好,咱们今天聊聊 journald。说实话,我刚从 syslog 转到 systemd-journald 那会儿,心里是有点抵触的。你想想看,用了十几年的 /var/log/messages,突然说改就改,谁不别扭?但后来在几个车载项目里被 journald 救过几次命之后,我彻底服气了。这玩意儿,真香。
3.1 systemd-journald 架构:它到底怎么工作的?
journald 是 systemd 家族的一员。它的核心思想很简单:把日志当成结构化数据来管,而不是纯文本。
我画个简化的架构图给你看:
应用程序 (app, daemon)
↓ 通过 libsystemd / syslog() / stdout
systemd-journald 服务
↓ 解析、索引、压缩
/run/log/journal/ (内存,默认)
/var/log/journal/ (持久化,需配置)
journald 接收日志的来源有好几个:
- 原生接口:通过 sd_journal_print() 这类 API 直接写入
- syslog 兼容:监听 /dev/log 套接字,老程序不用改
- 内核消息:从 /proc/kmsg 或 /dev/kmsg 抓取
- 标准输出:捕获服务的 stdout/stderr
嗯,这里要注意:默认情况下 journald 是把日志写在内存里的。这意味着什么?车一断电,日志就没了。我在项目里吃过这个亏,后面会讲怎么配持久化。
核心优势:journald 存的不只是文本,它还存了 _PID、_COMM、_SYSTEMD_UNIT 这些字段。你查日志的时候,可以像查数据库一样精确过滤。
3.2 journalctl 命令详解:这才是重头戏
journalctl 是咱们和 journald 打交道的唯一窗口。我个人习惯把它分成三类用法:查看、过滤、维护。
3.2.1 基本查看
# 查看所有日志(默认从旧到新)
journalctl
# 实时跟踪,类似 tail -f
journalctl -f
# 只看最近的 N 条
journalctl -n 50
# 只看某个服务的日志
journalctl -u my-service.service
我记得第一次在实车上用 journalctl -f 的时候,旁边坐着的测试同事都惊呆了。他以前查日志得 ssh 进去翻文件,现在直接实时看输出,爽得不行。
3.2.2 高级过滤:这才是 journald 的杀手锏
你想想看,在 syslog 时代,你想查「昨天下午3点到4点之间,某个进程报的 error」,你得 grep 半天。现在呢?一句话的事。
# 按时间范围过滤
journalctl --since "2024-01-15 14:00:00" --until "2024-01-15 15:00:00"
# 按优先级过滤(0=emerg, 1=alert, 2=crit, 3=err, 4=warning...)
journalctl -p err
# 按字段精确匹配
journalctl _PID=1234
journalctl _SYSTEMD_UNIT=network.service
journalctl _COMM=sshd
# 组合过滤:某个服务在某个时间段的错误
journalctl -u can-bus.service --since "1 hour ago" -p err
我的小技巧:查车载 CAN 总线相关问题时,我经常用 journalctl -u can-bus.service -p err --since "10 min ago" -o json-pretty。输出 JSON 格式,方便脚本解析。
3.2.3 输出格式控制
| 选项 | 说明 | 适用场景 |
|---|---|---|
-o short |
经典 syslog 风格 | 日常查看 |
-o verbose |
显示所有字段 | 深度调试 |
-o json |
JSON 格式 | 程序解析 |
-o cat |
只显示消息本体 | 管道处理 |
3.3 持久化日志配置:别让日志丢了
我曾经在一个车载项目中,遇到一个偶发性的蓝牙断连问题。测试跑了三天终于复现了,结果一查日志——空的。为什么?因为 journald 默认把日志写在内存里,车机重启后什么都没了。
那次之后,我养成了一个习惯:所有车载项目,第一件事就是配持久化日志。
3.3.1 开启持久化
方法很简单,创建目录并修改配置:
# 1. 创建持久化目录
mkdir -p /var/log/journal
# 2. 设置权限
systemd-tmpfiles --create --prefix /var/log/journal
# 3. 修改配置文件 /etc/systemd/journald.conf
# 找到或添加这一行:
Storage=persistent
# 4. 重启服务
systemctl restart systemd-journald
注意:如果 /var/log/journal 目录不存在,即使你配了 Storage=persistent,journald 也会自动回退到内存模式。所以第一步创建目录不能省。
3.3.2 其他关键配置项
在 /etc/systemd/journald.conf 里,我一般还会调这几个参数:
# 日志大小限制,车载环境存储有限
SystemMaxUse=500M
# 单文件大小
SystemMaxFileSize=100M
# 日志保留时间
MaxRetentionSec=7day
# 转发到 syslog(如果需要兼容老工具)
ForwardToSyslog=no
# 压缩旧日志
Compress=yes
嗯,这里有个坑:别把 SystemMaxUse 设得太小。我见过有人设 50M,结果日志频繁轮转,关键信息全被覆盖了。车载系统一般给 500M 到 1G 比较合理,具体看你的存储空间。
3.3.3 手动维护日志
# 查看当前日志使用量
journalctl --disk-usage
# 清理直到日志总量低于 200M
journalctl --vacuum-size=200M
# 清理超过 3 天的日志
journalctl --vacuum-time=3d
# 强制轮转(一般不用手动做)
journalctl --rotate
实战建议:在车载系统中,我通常会在 /etc/systemd/journald.conf 里配好 SystemMaxUse 和 MaxRetentionSec,然后就不管了。journald 会自动做轮转和清理。只有在调试阶段,我才会手动执行 --vacuum-size 来腾空间。
3.4 小结
journald 这套东西,说白了就是「结构化日志 + 强大过滤 + 自动管理」。比起老式的 syslog,它更适合车载这种需要快速定位问题的场景。
我个人建议你:
- 所有车载项目必开持久化,别省那点存储空间
- 学会用 journalctl 的过滤功能,特别是
-u、-p、--since这三个组合 - 配好日志大小限制,防止日志把存储撑爆
下一章咱们聊聊 syslog-ng 和 rsyslog,看看它们和 journald 怎么配合使用。到时候我会分享一个我在 T-Box 项目里遇到的「日志风暴」案例,挺有意思的。