systemd/journald 日志保留

systemd/journald 日志保留

我发现 journald 仅保留大约一个月的数据,尽管系统运行的时间比这长得多。这是什么原因造成的?

  • 我没有更改任何日志设置,也没有手动修改持久日志文件
  • 总体来说数据不多,journalctl | wc -l只有241313行。
  • 日志文件达到配置的限制(~300 MB),但 241k 行不可能占用 300 MB 的空间。
  • 我使用 /etc/systemd/journald.conf 中的默认设置,但以下设置除外SystemMaxUse=300M

答案1

总体来说数据不多,journalctl | wc -l只有241313行。

问题是,才不是由行组成,因此该命令实际上并不显示“总体”有多少数据。

journald 之所以是 journald(而不是简单地将另一个 syslogd 写入纯文本 .log 文件)的全部原因是每个 .journal 文件都是数据库,其中每个条目由各种“KEY=value”对组成:

  • 除了实际消息之外,它还存储各种进程信息(PID,UID,命令行,cgroup,systemd 单元名称......)。

  • 使用“/dev/log”系统日志 API 接收的消息会生成一些隐藏字段,其中一个字段包含未解析消息缓冲区的完整副本 - 基本上复制了已解析“MESSAGE”字段中的数据。(这显然是为了帮助调试,因为系统日志消息格式是非常定义较宽泛。

  • 使用“本机” journald API 的程序可以附加自己的字段,例如事件信息(地址、设备名称、单元名称,以及与消息相关的任何内容)。通常,API 会自动附加生成特定消息的源代码文件:行:函数。

  • 字段可以是多行的,甚至可以保存二进制数据。例如,描述进程转储核心的日志条目将在其“消息”中有多行堆栈跟踪。在某些较旧的 systemd 版本中,此类条目实际上包含整个多兆字节核心转储!(此功能后来被禁用。)

尝试journalctl -o exportjournalctl -o verbose更好地了解 systemd-journal 消息的样子。例如,您的“wc -l”看到的消息如下:

Nov 02 21:30:15 somehost ldap_child[2103938]: Failed to initialize credentials using keytab [MEMORY:/etc/krb5.keytab]: Cannot contact any KDC for realm 'EXAMPLE.COM'. Unable to create GSSAPI-encrypted LDAP connection.

实际存储起来更像这样:

__MONOTONIC_TIMESTAMP=5009050061377
__REALTIME_TIMESTAMP=1604345415827892
_BOOT_ID=50352af2d4aa4bc698628ee4e53e062a
_CAP_EFFECTIVE=3fffffffff
_COMM=ldap_child
_GID=0
_HOSTNAME=somehost
_MACHINE_ID=d7d18a5b04bbda0e66e8be55367049e2
_PID=2103938
_SOURCE_REALTIME_TIMESTAMP=1604345415825424
_SYSTEMD_CGROUP=/system.slice/sssd.service
_SYSTEMD_INVOCATION_ID=5407b2ce221b4abb8307f0664436fc5b
_SYSTEMD_SLICE=system.slice
_SYSTEMD_UNIT=sssd.service
_TRANSPORT=journal
_UID=0
CODE_FILE=src/providers/ldap/ldap_child.c
CODE_FUNC=ldap_child_get_tgt_sync
CODE_LINE=543
MESSAGE=Failed to initialize credentials using keytab [MEMORY:/etc/krb5.keytab]: Cannot contact any KDC for realm 'AD.EXAMPLE.COM'. Unable to create GSSAPI-encrypted LDAP connection.
PRIORITY=7
SSSD_DEBUG_LEVEL=10
SSSD_DOMAIN=ad.example.com
SSSD_PRG_NAME=ldap_child[2103938]
SYSLOG_IDENTIFIER=ldap_child

日志文件存储在二进制格式,而不是纯 ASCII 文本,因此还是会存在一些差异。一方面,一些普遍存在的字段(时间戳、启动 ID)存储为打包的 64 位值,其他字段在一定程度上进行了重复数据删除和 lz4 压缩。

另一方面,所有字段都经过索引以便按值进行有效查找(例如,可以通过“SYSLOG_IDENTIFIER=ldap_child”搜索日志),并且这些哈希表索引将占用基于文本的转储未考虑到的额外空间。

举一个具体的例子,包含 260 MB 文本消息的日志很容易就能容纳 15 倍的数据(二进制时间戳可能相差几 MB):

# journalctl -a | wc -l
2103207

# journalctl -a | wc -c | numfmt --to=iec
258M

$ journalctl -o export | grep -av ^__CURSOR | wc -c | numfmt --to=iec
1.6G

$ journalctl --header | awk '/Arena size:/ {a += $3} END {print a}' | numfmt --to=iec
2.4G

# du -hs /var/log/journal
2.4G    /var/log/journal

最后,如果我没记错的话,journald 还会为新条目(“竞技场”)预先分配空间,导致文件大于实际存储的数据量。

我不太清楚如何检查实际的数据存储在日志文件中,但请看以下示例,其中至少一半的 user.journal 是完全空的并且只是为将来的写入预先分配:

# du -hs user-2001.journal
8.1M    user-2001.journal

$ tail -c 4M user-2001.journal | hexdump -C
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00400000

相关内容