公众号旧文

删除日志时,为什么既不是按行也不是按大小

作者:胖子阅读 —

我刚执行了:

journalctl --vacuum-size=50M

随后马上产生了一个疑问:

  • 是不是按日志行数删除?
  • 是不是按字节数删除?

如果是按行数删除,那么剩余日志大小,肯定不是正好的 50M。

如果是按字节数删除,保留最近的50M,那么日志肯定会被截断。

实际上,systemd-journald 的工作方式和传统文本日志完全不同。

一、很多人会误以为日志是文本

例如 /var/log/syslog、/var/log/messages 这样的日志文件,本质上就是文本:

Jul 12 10:00:01 kernel: ...
Jul 12 10:00:02 sshd: ...
Jul 12 10:00:03 systemd: ...

如果是文本文件,那么理论上可以:

  • 删除前 1000 行
  • 删除前 10MB
  • 删除指定时间以前的内容

因为文本可以从任意位置截断。

很多日志管理工具(例如 logrotate)就是这样工作的。

二、但 journald 根本不是文本

systemd-journald 保存的是一种专门设计的二进制格式。

例如:

/var/log/journal/
└── 9f7b2d9c1d8f4f3d8b0d...
    ├── system@0000000000000001.journal
    ├── system@0000000000000002.journal
    ├── system@0000000000000003.journal
    └── system.journal

这里每一个 .journal 文件都是一个完整的数据文件,而不是普通文本。

这些文件内部不仅保存日志内容,还包含:

  • 索引(Index)
  • 时间戳
  • 元数据(Metadata)
  • 校验信息(Checksum)
  • 对象引用(Object)
  • 哈希表(Hash Table)

因此,它不能像文本一样随意删除中间的一部分。

三、Vacuum 的删除单位是什么?

答案是:

整个 Journal 文件。

例如当前有四个日志文件:

文件 大小
A.journal 20MB
B.journal 18MB
C.journal 25MB
D.journal 57MB

总大小:

120MB

现在执行:

journalctl --vacuum-size=50M

可能有些人会认为系统将:

删掉 A 的一半
删掉 B 的三分之一

实际上并不会。

真正删除的是:

整个 A.journal

如果仍然超过限制:

再继续删除整个 B.journal

直到满足限制条件。

因此,删除单位始终是整个 Journal 文件。

四、为什么不会精确等于 50MB?

这是因为 Journald 存在两种日志文件。

Archived Journal(归档日志)

已经关闭,不再写入。

这些文件可以直接删除。

例如:

system@0001.journal
system@0002.journal
system@0003.journal

Active Journal(活动日志)

当前仍然在写入。

例如:

system.journal

这个文件正在不断追加新的日志。

它不能删除,更不能从中间截断。

否则整个 Journal 文件都会损坏。

因此,Journald 永远不会:

  • 删除活动文件的一半
  • 删除活动文件前 10MB
  • 删除活动文件中的部分记录

五、一个实际例子

假设当前目录如下:

文件 大小
A 20MB
B 18MB
C 25MB
D(当前正在写) 57MB

总大小:

120MB

执行:

journalctl --vacuum-size=50M

删除过程可能是:

删除 A
剩余 100MB
删除 B
剩余 82MB
删除 C
剩余 57MB

此时虽然仍然超过 50MB,

但是:

57MB

已经全部来自当前活动日志文件。

因为它不能被拆开,所以 Journald 会停止删除。

因此最终结果可能就是:

57MB

而不是:

50MB

这是完全正常的。

六、为什么不能按 MB 精确删除?

有人会问:

既然知道目标是 50MB,为什么不能把最后一个文件删掉 7MB?

原因很简单:

.journal 文件并不是一个连续的文本。

它内部由大量对象组成:

Header
Object
Object
Hash Table
Index
Data
Entry
Field
...

所有数据之间都有索引关系。

如果随意删除中间一部分:

比如删掉前 7MB

那么:

  • 索引全部失效
  • 哈希损坏
  • 校验失败
  • 整个 Journal 文件无法读取

因此,Journald 从设计上就不支持这种操作。

七、SystemMaxUse 真正的含义

可能会有人把:

SystemMaxUse=50M

理解成日志一定保持 50MB。

实际上更准确的理解应该是:

尽量将 Journal 总大小控制在约 50MB 附近。

它并不是一个绝对值。

实际大小可能是:

48MB
49MB
50MB
53MB
57MB

都属于正常现象。

因为 Journald 始终优先保证日志文件的完整性,而不是追求精确的数字。