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 里配好 SystemMaxUseMaxRetentionSec,然后就不管了。journald 会自动做轮转和清理。只有在调试阶段,我才会手动执行 --vacuum-size 来腾空间。

3.4 小结

journald 这套东西,说白了就是「结构化日志 + 强大过滤 + 自动管理」。比起老式的 syslog,它更适合车载这种需要快速定位问题的场景。

我个人建议你:

  1. 所有车载项目必开持久化,别省那点存储空间
  2. 学会用 journalctl 的过滤功能,特别是 -u-p--since 这三个组合
  3. 配好日志大小限制,防止日志把存储撑爆

下一章咱们聊聊 syslog-ng 和 rsyslog,看看它们和 journald 怎么配合使用。到时候我会分享一个我在 T-Box 项目里遇到的「日志风暴」案例,挺有意思的。