4. systemd 日志系统:从入门到实战

聊到 Linux 日志,很多人第一反应是 /var/log/messages。但在我日常排障中,systemd-journald 才是真正的利器。它把内核日志、系统服务日志、用户应用日志全部收拢到一起,用 journalctl 一把抓。今天我就带你把它吃透。

一句话总结:systemd 日志系统 = journald(采集引擎)+ journalctl(查询工具)。它比传统 syslog 更结构化、更安全、更易查。

4.1 journalctl 基础用法

先来个最直接的——看所有日志:

journalctl

这命令会输出从开机到现在所有日志,量很大。我一般不会这么用,而是加参数过滤。

4.1.1 按时间过滤

排障时最常用的就是看“刚才发生了什么”。比如:

# 查看本次启动以来的日志
journalctl -b

# 查看上一次启动的日志(排重启故障时很有用)
journalctl -b -1

# 只看最近 10 分钟的
journalctl --since "10 min ago"

# 指定时间范围
journalctl --since "2025-03-01 08:00" --until "2025-03-01 12:00"

我个人习惯用 --since "1 hour ago" 这种相对时间,省得算时区。

4.1.2 按服务/单元过滤

想看某个服务的日志?加 -u:

# 只看 sshd 的日志
journalctl -u sshd

# 同时看两个服务
journalctl -u nginx -u mysql

我曾经遇到一个 case:Nginx 频繁 502,但错误日志里啥也没有。用 journalctl -u nginx -f 实时跟踪,才发现是 upstream 的 PHP-FPM 进程被 OOM killer 干掉了。嗯,这种跨服务的关联,传统日志文件很难一眼看出来。

4.1.3 实时跟踪与翻页

# 类似 tail -f
journalctl -f

# 分页查看(默认行为,适合阅读)
journalctl | less

# 输出最后 50 行
journalctl -n 50

小技巧:排障时我常用 journalctl -u <服务名> -f --since "5 min ago",既能看到历史,又能实时跟踪新日志。

4.2 日志优先级:一眼识别严重程度

日志不是平起平坐的。systemd 沿用了 syslog 的 8 级优先级,从 0(emerg)到 7(debug)。你想想看,生产环境里如果满屏都是 debug 信息,真正的问题就被淹没了。

优先级 数值 含义 典型场景
emerg 0 系统不可用 内核 panic、系统崩溃
alert 1 必须立即处理 磁盘损坏、数据库损坏
crit 2 严重错误 硬件故障、服务完全不可用
err 3 错误 配置错误、连接失败
warning 4 警告 磁盘空间不足、重试成功
notice 5 正常但重要 服务启动/停止、用户登录
info 6 一般信息 请求处理、状态更新
debug 7 调试信息 开发/排障时使用

用 -p 参数按优先级过滤:

# 只看 err 及以上级别(0-3)
journalctl -p err

# 只看 warning 及以上(0-4)
journalctl -p warning

# 指定范围:只看 err 到 crit
journalctl -p err..crit

避坑指南:我曾经在生产环境用 journalctl -p debug 查问题,结果日志量太大直接把终端卡死了。建议先 -n 100 限制行数,或者配合 --since 缩小时间范围。

4.3 日志字段解析:结构化数据的威力

传统日志是一行字符串,你得靠正则去抠信息。systemd 日志不一样——每条日志都是一组 结构化字段(key=value)。这才是它最强大的地方。

4.3.1 查看完整字段

# 显示所有字段(verbose 模式)
journalctl -u sshd -o verbose

# 输出示例(部分字段)
  _TRANSPORT=syslog
  _PID=1234
  _UID=0
  _GID=0
  _COMM=sshd
  _EXE=/usr/sbin/sshd
  _CMDLINE=/usr/sbin/sshd -D
  _SYSTEMD_UNIT=sshd.service
  _BOOT_ID=abc123...
  MESSAGE=Accepted publickey for root from 192.168.1.100 port 22
  PRIORITY=6
  SYSLOG_FACILITY=10

4.3.2 常用字段速查

字段名 含义 排障用途
MESSAGE 日志正文 核心信息
PRIORITY 优先级(0-7) 过滤严重级别
_PID 进程 ID 追踪具体进程
_UID 用户 ID 判断是哪个用户触发的
_SYSTEMD_UNIT 所属 systemd 单元 定位到具体服务
_BOOT_ID 启动 ID 区分不同启动周期
_TRANSPORT 日志来源(syslog/stdout/kernel) 判断日志产生方式
_CMDLINE 完整命令行 确认启动参数

4.3.3 按字段过滤

这才是 journalctl 的杀手锏:

# 找 PID 为 1234 的进程日志
journalctl _PID=1234

# 找某个用户触发的日志
journalctl _UID=1000

# 组合过滤:找 sshd 服务中 PID 为 1234 的日志
journalctl _SYSTEMD_UNIT=sshd.service _PID=1234

# 找内核日志
journalctl _TRANSPORT=kernel

你想想看,传统方式要查“某个 PID 在某个时间段的日志”,得 grep 半天。用 journalctl 一条命令搞定。

4.4 日志持久化:别让日志丢了

默认情况下,journald 把日志存在内存(/run/log/journal)。好处是快,不伤磁盘。坏处是——重启就没了。

我记得有一次排查一个“开机后服务启动失败”的问题,因为日志没持久化,重启后现场全丢了,只能靠猜。从那以后,我所有服务器都做了持久化配置。

4.4.1 开启持久化

创建持久化目录,然后重启服务:

# 创建持久化目录
mkdir -p /var/log/journal

# 设置正确的权限
systemd-tmpfiles --create --prefix /var/log/journal

# 重启 journald
systemctl restart systemd-journald

验证是否生效:

# 查看日志存储路径
journalctl --header | grep "Journal path"

# 正常输出应该是
# Journal path: /var/log/journal/机器ID/system.journal

4.4.2 配置日志大小与保留策略

持久化后要注意磁盘空间。我见过有人没配限制,结果日志把根分区撑爆了。配置文件在 /etc/systemd/journald.conf:

[Journal]
# 日志最大体积(默认 10% 分区大小)
SystemMaxUse=500M

# 单个日志文件最大
SystemMaxFileSize=100M

# 保留最近 2 周的日志
MaxRetentionSec=2week

# 日志文件达到上限后,删除最旧的 20%
SystemKeepFree=20%

改完配置记得重启:

systemctl restart systemd-journald

我的建议:生产环境配 SystemMaxUse=1G + MaxRetentionSec=1month,既能保留足够长的历史,又不会撑爆磁盘。如果日志量特别大(比如业务日志),建议单独分一个分区给 /var/log。

4.4.3 手动清理日志

有时候磁盘告警了,需要紧急腾空间:

# 只保留最近 3 天的
journalctl --vacuum-time=3d

# 只保留 200M 日志
journalctl --vacuum-size=200M

# 只保留最近 5 个日志文件
journalctl --vacuum-files=5

4.5 知识体系总览

下面这张图把 systemd 日志系统的核心脉络串起来了,建议你保存下来当速查表:

systemd 日志系统知识体系 systemd 日志系统核心脉络 内核日志 系统服务日志 用户应用日志 systemd-journald 结构化日志存储(.journal 文件) journalctl 查询工具 按时间过滤 按服务过滤 按优先级过滤 按字段过滤 💡 持久化配置: /var/log/journal 目录 journald.conf 策略控制

4.6 实战排障场景

最后分享一个我常用的排障流程,遇到服务异常时按这个顺序查:

  1. 看服务状态:systemctl status <服务名> —— 先确认是不是 systemd 层面的问题
  2. 看最近日志:journalctl -u <服务名> -n 50 --since "30 min ago" —— 缩小范围
  3. 看错误级别:journalctl -u <服务名> -p err --since "1 hour ago" —— 只看关键错误
  4. 看关联服务:journalctl _PID=<异常PID> -o verbose —— 深挖具体进程
  5. 看系统整体:journalctl -p emerg..err -b —— 检查本次启动有没有系统级问题

这套流程我用了好几年,90% 的服务故障都能在 5 分钟内定位到根因。你试试看,用熟了之后,你会觉得传统日志文件就像回到了石器时代。