强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

systemd 教程 / 日志高级配置(journald)

日志高级配置(journald)

概述

systemd-journald 是 systemd 的日志服务守护进程,负责收集、存储和管理系统日志。与传统的 rsyslog 不同,journald 使用二进制格式存储日志,支持结构化查询、速率限制、日志签名等高级功能。本文深入介绍 journald 的高级配置和生产实践。


journald.conf 配置详解

配置文件位置

/etc/systemd/journald.conf          # 主配置文件
/etc/systemd/journald.conf.d/*.conf # drop-in 配置
/run/systemd/journald.conf.d/*.conf # 运行时配置

[Journal] 段完整参数

# /etc/systemd/journald.conf
[Journal]
# 存储模式
Storage=auto

# 持久化目录
# SystemJournalPath=/var/log/journal
# RuntimeJournalPath=/run/log/journal

# 压缩
Compress=yes

# 日志大小限制
SystemMaxUse=500M
SystemKeepFree=1G
SystemMaxFileSize=50M
SystemMaxFiles=100

# 运行时日志大小限制
RuntimeMaxUse=200M
RuntimeKeepFree=100M
RuntimeMaxFileSize=25M
RuntimeMaxFiles=50

# 日志保留时间
MaxRetentionSec=3month
MaxFileSec=1week

# 速率限制
RateLimitIntervalSec=30s
RateLimitBurst=10000

# 转发到 console
ForwardToConsole=no
ForwardToWall=yes

# TTY
# TTYPath=/dev/console

# 日志级别
# MaxLevelStore=debug
# MaxLevelSyslog=debug
# MaxLevelConsole=notice
# MaxLevelWall=emerg

# 同步写入
SyncIntervalSec=5m

# 分片大小
SplitMode=uid

# Seal(前向安全密封)
Seal=yes

# 读取权限
ReadKMsg=yes

# Audit
Audit=yes

存储模式详解

Storage 参数

说明
volatile仅存储在内存中(/run/log/journal/
persistent持久化存储到磁盘(/var/log/journal/
auto如果 /var/log/journal/ 存在则持久化,否则仅内存
none不存储日志(不推荐)

volatile 模式

日志仅存储在内存中,重启后丢失:

[Journal]
Storage=volatile
RuntimeMaxUse=200M

适用场景:

  • 临时系统或容器
  • 日志已转发到远程服务器
  • 资源受限的嵌入式设备

persistent 模式

日志持久化到磁盘,重启后保留:

# 创建持久化日志目录
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal

# 重启 journald 生效
sudo systemctl restart systemd-journald
[Journal]
Storage=persistent
SystemMaxUse=2G
SystemKeepFree=4G
SystemMaxFileSize=100M

💡 提示:在生产环境中,建议使用 persistent 模式,便于日志审计和故障排查。


日志大小管理

参数说明

参数说明建议值
SystemMaxUse持久化日志最大使用空间500M ~ 4G
SystemKeepFree保持的最小可用空间1G ~ 5G
SystemMaxFileSize单个日志文件最大大小50M ~ 200M
SystemMaxFiles日志文件最大数量50 ~ 200
RuntimeMaxUse运行时日志最大空间100M ~ 500M
RuntimeKeepFree运行时保持的可用空间50M ~ 200M
MaxRetentionSec日志保留时间3month / 6month / 1year
MaxFileSec单个日志文件的时间跨度1day / 1week / 1month

生产环境配置示例

# /etc/systemd/journald.conf.d/production.conf
[Journal]
Storage=persistent
Compress=yes

# 限制日志总大小
SystemMaxUse=4G
SystemKeepFree=10G
SystemMaxFileSize=200M
SystemMaxFiles=50

# 保留 6 个月
MaxRetentionSec=6month
MaxFileSec=1week

# 速率限制
RateLimitIntervalSec=30s
RateLimitBurst=20000
# 查看当前日志大小
journalctl --disk-usage

# 查看日志统计信息
journalctl --header

手动清理日志

# 清除所有日志(危险!)
sudo journalctl --vacuum-time=1s

# 保留最近 7 天的日志
sudo journalctl --vacuum-time=7d

# 限制日志大小为 500M
sudo journalctl --vacuum-size=500M

# 限制日志文件数量
sudo journalctl --vacuum-files=10

# 清除特定服务的日志
sudo journalctl --unit=nginx.service --vacuum-time=1d

速率限制

防止日志风暴

当服务出现错误循环时,可能会产生大量日志,导致磁盘空间耗尽:

[Journal]
# 30 秒内最多 10000 条日志
RateLimitIntervalSec=30s
RateLimitBurst=10000

参数说明

参数说明默认值
RateLimitIntervalSec速率限制的时间窗口30s
RateLimitBurst时间窗口内最大日志条数10000

查看被限制的日志

# 查看 journald 自身状态
journalctl --header

# 查看被丢弃的日志条数
sudo journalctl -b | grep "Suppressed"

⚠️ 注意:如果日志被速率限制,会在日志中出现类似以下消息:

Jun 15 10:23:45 hostname systemd-journald[123]: Suppressed 5432 messages from user.slice

调整速率限制

对于日志量大的服务(如 Web 服务器),可以适当提高限制:

[Journal]
RateLimitIntervalSec=10s
RateLimitBurst=50000

💡 提示:如果需要完全禁用速率限制(不推荐),设置 RateLimitBurst=0


日志转发

转发到控制台

[Journal]
ForwardToConsole=yes
TTYPath=/dev/console
MaxLevelConsole=info

转发到 syslog

[Journal]
ForwardToSyslog=yes

转发到内核日志缓冲区

[Journal]
ForwardToKMsg=no

转发到 wall(终端广播)

[Journal]
ForwardToWall=yes

远程日志

systemd-journal-remote

将日志从多台机器集中到一台日志服务器。

日志服务器配置

# 安装 systemd-journal-remote
sudo apt install systemd-journal-remote  # Debian/Ubuntu
sudo dnf install systemd-journal-remote  # Fedora/RHEL
# /etc/systemd/journal-remote.conf
[Remote]
# 监听端口
SplitMode=host
ServerKeyFile=/etc/ssl/private/journal-remote.key
ServerCertificateFile=/etc/ssl/certs/journal-remote.pem
TrustedCertificateFile=/etc/ssl/ca/trusted.pem
# 启动接收服务
sudo systemctl enable --now systemd-journal-remote.socket
sudo systemctl enable --now systemd-journal-remote.service

客户端配置(上传日志)

# /etc/systemd/journal-upload.conf
[Upload]
URL=https://logserver.example.com:19532
ServerKeyFile=/etc/ssl/private/journal-upload.key
ServerCertificateFile=/etc/ssl/certs/journal-upload.pem
TrustedCertificateFile=/etc/ssl/ca/trusted.pem
# 启动上传服务
sudo systemctl enable --now systemd-journal-upload.service

简化 HTTP 模式

不使用 TLS 的简化配置(仅适合内网):

# /etc/systemd/journal-upload.conf
[Upload]
URL=http://logserver.example.com:19531

⚠️ 注意:HTTP 模式不加密,仅适合可信内网环境。


日志签名(Forward Secure Sealing)

概念

Forward Secure Sealing(FSS)为日志提供完整性验证,确保日志在写入后不能被篡改。

配置 FSS

# 生成密封密钥
sudo journalctl --setup-keys

# 输出:
# Accepted sealing key is stored in /var/log/journal/*/fss.
# Verification key is: a]xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx
# Please write down the verification key and store it in a safe place.

# 验证日志完整性
sudo journalctl --verify

# 使用验证密钥验证
sudo journalctl --verify-key=xxxx-xxxx-xxxx-xxxx
# /etc/systemd/journald.conf
[Journal]
Seal=yes

⚠️ 注意:验证密钥必须安全保存,否则无法验证日志完整性。


日志导出与备份

导出为文本格式

# 导出所有日志
journalctl > all-logs.txt

# 导出特定时间段的日志
journalctl --since "2026-01-01" --until "2026-01-31" > january-logs.txt

# 导出特定服务的日志
journalctl -u nginx.service > nginx-logs.txt

# 导出本次启动的日志
journalctl -b > boot-logs.txt

导出为 JSON 格式

# JSON 格式导出
journalctl -o json > logs.json

# JSON-pretty 格式
journalctl -o json-pretty > logs-pretty.json

# 使用 jq 查询 JSON 日志
journalctl -o json | jq 'select(.PRIORITY <= 3)'

导出为二进制格式

# 导出原始日志文件
sudo cp -r /var/log/journal/ /backup/journal-backup/

# 仅导出特定机器的日志
sudo cp /var/log/journal/$(machine-id) /backup/

从备份恢复

# 将备份日志放到临时目录
sudo journalctl --directory=/backup/journal-backup/ --file=myfile.journal

# 搜索备份日志
sudo journalctl --directory=/backup/journal-backup/ -u nginx.service

日志 API(libsystemd sd_journal)

C 语言示例

#include <systemd/sd-journal.h>
#include <stdio.h>

int main(void) {
    sd_journal *j;
    int r;

    // 打开日志
    r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
    if (r < 0) {
        fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
        return 1;
    }

    // 添加过滤条件
    sd_journal_add_match(j, "UNIT=nginx.service", 0);

    // 遍历日志
    SD_JOURNAL_FOREACH(j) {
        const char *data;
        size_t length;

        sd_journal_get_data(j, "MESSAGE", (const void **)&data, &length);
        printf("%.*s\n", (int)length, data);
    }

    sd_journal_close(j);
    return 0;
}
# 编译
gcc -o journal-reader journal-reader.c -lsystemd

Python 示例

import subprocess
import json

def query_journal(unit=None, since=None, priority=None):
    cmd = ['journalctl', '-o', 'json', '--no-pager']
    if unit:
        cmd.extend(['-u', unit])
    if since:
        cmd.extend(['--since', since])
    if priority:
        cmd.extend(['-p', str(priority)])

    result = subprocess.run(cmd, capture_output=True, text=True)
    logs = []
    for line in result.stdout.strip().split('\n'):
        if line:
            logs.append(json.loads(line))
    return logs

# 使用示例
errors = query_journal(unit='nginx.service', priority=3, since='1 hour ago')
for entry in errors:
    print(f"[{entry.get('_HOSTNAME', 'unknown')}] {entry.get('MESSAGE', '')}")

sd_journal API 关键函数

函数说明
sd_journal_open()打开日志
sd_journal_close()关闭日志
sd_journal_next()移动到下一条
sd_journal_get_data()获取字段数据
sd_journal_add_match()添加过滤条件
sd_journal_add_disjunction()添加 OR 条件
sd_journal_seek_head()跳转到开头
sd_journal_seek_tail()跳转到末尾
sd_journal_get_realtime_usec()获取时间戳
sd_journal_get_cutoff_realtime_usec()获取日志时间范围

日志集中收集方案

方案 1:ELK Stack(Elasticsearch + Logstash + Kibana)

systemd-journald ──▶ journal-remote ──▶ Logstash ──▶ Elasticsearch ──▶ Kibana

方案 2:Loki + Grafana

# 使用 promtail 收集日志
# promtail 配置
scrape_configs:
  - job_name: journal
    journal:
      json: false
      max_age: 12h
      path: /var/log/journal
      labels:
        job: systemd-journal
    relabel_configs:
      - source_labels: ['__journal__systemd_unit']
        target_label: 'unit'

方案 3:rsyslog 转发

# /etc/rsyslog.d/journal.conf
# 从 journald 读取日志
$ModLoad imjournal
$WorkDirectory /var/lib/rsyslog
$StateFile imjournal.state
$IMJournalStateFile imjournal.state

# 转发到远程 syslog
*.* @@logserver.example.com:514

常用命令汇总

# 查看日志
journalctl                          # 所有日志
journalctl -b                       # 本次启动
journalctl -b -1                    # 上次启动
journalctl -u nginx.service         # 特定服务
journalctl -p err                   # 错误级别
journalctl --since "1 hour ago"     # 最近1小时
journalctl --since "2026-01-01"     # 指定日期起
journalctl -f                       # 实时跟踪

# 日志管理
journalctl --disk-usage             # 查看磁盘占用
journalctl --vacuum-time=7d         # 清理旧日志
journalctl --vacuum-size=500M       # 限制大小
journalctl --verify                 # 验证完整性

# 输出格式
journalctl -o short                 # 短格式(默认)
journalctl -o verbose               # 详细格式
journalctl -o json                  # JSON 格式
journalctl -o json-pretty           # JSON 格式化
journalctl -o cat                   # 仅消息内容

# 服务管理
sudo systemctl restart systemd-journald
sudo systemctl status systemd-journald

扩展阅读