第4章 日志采集策略:syslog、journalctl、dmesg、应用日志、时间窗口、关联ID

现象识别:无日志不诊断

后台任务被系统终止后,工程师面临的第一道屏障往往是日志缺失或数据孤岛。仅凭“进程消失了”这一表象无法区分 OOM Killer、systemd 强制终止、容器资源超限还是外部信号介入。系统化日志采集策略的核心目标是在终止事件发生前后,从多个独立源捕获证据,并通过时间窗口与关联 ID 将碎片信息拼接为完整的事故时间线。

最佳实践: 在故障排查初期,应依次检查 dmesg(内核环形缓冲区)、journalctl(systemd 日志)、应用自定义日志。跳过任何一步都可能导致关键线索遗漏。
后台任务终止日志关联流程 多源日志关联流程 后台任务终止事件 内核/系统日志 (dmesg / OOM) Systemd Journal (journalctl) 应用自定义日志 (文件 / stdout) 采集与过滤 集中关联与分析 核心:统一时间窗口 (NTP) + 关联 ID (UUID) 确保跨源数据联动

日志源矩阵:数据采集清单

不同日志源覆盖不同的系统层。诊断后台任务终止时,必须联合查询以下四个核心源:

日志源 默认存储 采集命令/路径 时间戳精度 是否记录 Signal/OOM
内核环形缓冲区 内存 (dmesg)
dmesg -T
秒级(依赖 -T 转换) 是(OOM Killer、内存不足、信号投递)
Systemd Journal /run/log/journal(内存)
/var/log/journal(持久化)
journalctl -u <unit>
微秒级 是(进程退出码、信号、cgroup 清理)
Syslog /var/log/messages
/var/log/syslog
grep <keyword> /var/log/syslog
秒级 部分(依赖 rsyslog 配置)
应用日志 自定义路径(如 /var/log/app/)
cat /var/log/app/error.log
毫秒/微秒级(应用自定) 间接(需主动捕获 stderr)
陷阱提示: 默认情况下 journald 的日志存储在内存中,重启后丢失。生产环境必须启用持久化,否则一旦被终止的后台任务导致系统重启,关键日志将彻底消失。

判断规则:故障树式逐层查证

面对后台任务终止,按以下优先级判断日志源,避免在无效源上浪费时间:

  1. 第一层:dmesg(内核态证据)——检查是否有 OOM Killer 或硬件错误。
    dmesg -T | grep -iE "killed|oom|out of memory"
  2. 第二层:journalctl(系统态证据)——检查 systemd 是否主动终止进程。
    journalctl -u demo.service --since "5 minutes ago" -o verbose
    关注 MAIN_PID=、EXIT_CODE=、EXIT_STATUS= 字段。
  3. 第三层:应用日志(用户态证据)——检查应用自身是否捕获到终止信号。
    grep "terminated\|SIGTERM\|SIGKILL\|fatal" /var/log/app/app.log

关联 ID:串联碎片化日志的线索

分布式后台任务通常横跨多个微服务或主机。没有关联 ID,即使日志齐全也无法还原调用链。

  • 执行前注入:在任务启动时生成 UUID,写入环境变量或请求头。
    CORRELATION_ID=$(uuidgen)
    echo "[$(date)] Starting task with CID=$CORRELATION_ID" >> /var/log/app/init.log
    systemd-run --unit=demo-$CORRELATION_ID /opt/bin/demo-task
  • 日志输出格式:所有日志行必须包含关联 ID。
    <time> [<LEVEL>] [CID:=a1b2c3d4] 任务处理完成
  • 跨源 grep:在排查时直接用关联 ID 过滤所有日志源。
    journalctl -u demo-* | grep "a1b2c3d4"
    grep "a1b2c3d4" /var/log/app/*.log
    dmesg -T | grep "a1b2c3d4"  # 内核日志也注入CID(高级场景)
实施要点:
  1. 时间窗口必须采用 UTC 时间,所有服务器统一 NTP 源,防止时区偏差导致日志乱序。
  2. 关联 ID 建议放置在日志行的固定位置(如第 5 列),便于自动化工具解析。
  3. 怀疑 OOM 时,优先查询 dmesg -T | tail -100,因为 journald 可能因内存压力未能及时写入。

修复动作:日志系统加固

当发现以下隐患时,应立即调整采集策略:

发现症状 修复动作 验证方法
journald 日志未持久化
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald
ls /var/log/journal 存在有效目录
应用日志无时间戳 修改日志格式 log_format='%(asctime)s %(message)s' 检查日志行是否以 2025-05-14 10:00:00 开头
缺少 OOM 记录 启用 vm.oom_dump_tasks=1 并配置 abrt 服务 sysctl vm.oom_dump_tasks 返回 1
日志轮转导致历史丢失 配置 logrotate: rotate 4; maxsize 100M; compress; cat /etc/logrotate.d/ 包含自定义规则

复测:通过模拟终止验证日志采集完整性

执行以下步骤复测日志系统是否就绪:

  1. 启动一个测试后台任务:systemd-run --unit=test-logging /bin/sleep 120
  2. 强制终止:systemctl kill -s SIGKILL test-logging
  3. 采集证据链:
    # 查看系统d日志
    journalctl -u test-logging --no-pager | grep -i kill
    # 查看内核日志
    dmesg -T | tail -5
    # 查看应用日志(如果能捕获到信号)
    # 应能看到 EXIT_CODE=KILLED 或 signal 9
  4. 确认所有源均记录同一事件,且时间窗口误差不超过 1 秒。

预防:标准化日志采集清单

每个新后台任务上线前,强制检查以下项目:

检查项 要求 合规标准
日志持久化 journald 写入磁盘 /var/log/journal 存在
关联 ID 注入 每次任务启动生成 UUID 日志行包含 CID= 字段
时间戳同步 所有节点 NTP 一致 timedatectl status | grep "NTP service: active"
日志级别覆盖 应用启动时至少打印 INFO/ERROR 级别 日志目录存在且最新更新时间小于 1 小时
常见失败模式:
  • 只查了应用日志,忽略了 dmesg 中的 OOM 记录,导致错误判断为程序 bug 而非资源不足。
  • journalctl 默认只显示当前启动的日志,journalctl -u demo.service 若未加时间范围可能只看到最近几十条记录,遗漏早期异常。
  • 关联 ID 仅在单个服务内部传递,未跨进程透传,导致上下游日志断裂。