Appearance
11|日志管理
服务起不来、接口报 500、登录不了——出问题第一反应是看日志。但日志在哪?systemd 管的服务日志在 journald 里,传统系统日志在 /var/log/messages,Nginx 的访问日志在自己文件里。位置不同、看的方式也不同。
本篇讲清楚 Linux 上日志分几类、各自在哪看,以及怎么用 logrotate 防止日志把磁盘撑爆。
Linux 上的日志来源主要分三类:
| 来源 | 内容 | 查看方式 |
|---|---|---|
| systemd journald | systemd 管理的服务输出、内核消息、系统日志 | journalctl |
| rsyslog | 传统系统日志(/var/log/messages、/var/log/secure 等) | 文本工具(cat、grep、less) |
| 应用文件日志 | 应用自己写入的文件(如 /var/log/nginx/access.log) | 文本工具 |
一、传统日志文件位置
绝大多数系统和服务的日志集中在 /var/log 下,但不同发行版的文件命名不一致,排查前需要先确认目标机器属于哪个系列:
| 文件 | 发行版 | 内容 |
|---|---|---|
/var/log/messages | RHEL 系 | 通用系统日志 |
/var/log/syslog | Debian / Ubuntu | 通用系统日志 |
/var/log/secure | RHEL 系 | 认证与安全相关(SSH 登录、sudo) |
/var/log/auth.log | Debian / Ubuntu | 认证与安全相关 |
/var/log/dmesg | 通用 | 内核启动时的日志快照 |
/var/log/cron | 部分发行版 | cron 执行日志 |
/var/log/yum.log 或 /var/log/dnf.log | RHEL 系 | 包管理操作日志 |
/var/log/apt/history.log | Debian / Ubuntu | apt 操作日志 |
按问题类型快速定位:
- 登录失败、SSH 异常、sudo 失败 — 查
/var/log/secure或/var/log/auth.log - 磁盘错误、网卡 down、内存 OOM — 查
dmesg - 服务异常退出 — 查
journalctl -u <服务名> - 定时任务未执行 — 查
/var/log/cron(或journalctl -u cron)
二、journald 与 journalctl
systemd 自带的日志系统称为 journald,它收集以下来源的日志,统一存储在二进制格式的日志文件中:
- systemd 管理的服务的 stdout / stderr
- 内核消息(等同于 dmesg 来源)
- 系统启动日志
- syslog 兼容日志
由于存储格式不是纯文本,不能直接 cat,必须通过 journalctl 查询。
journalctl 常用参数
bash
journalctl # 查看全部日志(通常非常多,不直接这么用)
journalctl -u nginx # 只看指定 unit 的日志
journalctl -u nginx -f # 持续跟踪(类似 tail -f)
journalctl -u nginx -n 100 # 最近 100 行
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --since "2026-06-23 10:00" --until "2026-06-23 11:00"
journalctl -p err # 只看 error 级别及以上
journalctl -p warning -p err # 只看 warning 和 error
journalctl -k # 内核日志(等同于 dmesg)
journalctl --no-pager # 不分页,适合脚本处理或重定向日志级别
-p 参数支持的日志级别(从严重到低):
| 数字 | 名称 | 含义 |
|---|---|---|
| 0 | emerg | 系统不可用 |
| 1 | alert | 必须立即处理 |
| 2 | crit | 严重故障 |
| 3 | err | 错误 |
| 4 | warning | 警告 |
| 5 | notice | 注意 |
| 6 | info | 信息 |
| 7 | debug | 调试 |
-p err 等同于 -p 3,会返回 0-3 级别的日志(即 emerg、alert、crit、err)。
服务启动失败的标准排查路径
服务启动失败时,以下两条命令应当形成肌肉记忆:
bash
# 第 1 步:看当前状态和最后几行日志
systemctl status app --no-pager
# 第 2 步:看最近 100 行完整日志
journalctl -u app -n 100 --no-pager从最近的日志开始,按需要逐步扩大时间范围。不要一上来就翻全量日志——一个运行了半年的服务,journal 中可能积累 GB 级日志,从头翻完全无法实现。
journald 的持久化陷阱
默认情况下,如果 /var/log/journal 目录不存在,journald 仅将日志存储在 /run/log/journal(基于 tmpfs,实际在内存中),重启后日志全部丢失。
排查"重启前的日志找不到"这类问题时,根本原因通常就是这个。
开启持久化:
bash
# 创建持久化目录
mkdir -p /var/log/journal
# 重启 journald 让它感知到新目录
systemctl restart systemd-journald
# 验证
journalctl --disk-usage # 应该显示在 /var/log/journal 下使用了多少空间或在配置文件中显式开启:
bash
# /etc/systemd/journald.conf
[Journal]
Storage=persistent # auto(默认)| volatile(内存)| persistent(磁盘)| none
SystemMaxUse=2G # 限制总占用空间
SystemMaxFileSize=50M # 单个日志文件大小
MaxRetentionSec=1month # 最多保留多久修改后重启 journald:
bash
systemctl restart systemd-journaldjournald 磁盘空间管理
journald 默认会占用磁盘可用空间的 10%,可能在某些情况下占用较多空间:
bash
# 查看 journal 当前磁盘占用
journalctl --disk-usage
# 清理 30 天前的日志
journalctl --vacuum-time=30d
# 限制最大保留 1GB
journalctl --vacuum-size=1G三、rsyslog
rsyslog 是传统的系统日志服务,负责按规则把不同来源的日志写入对应的文本文件(如 /var/log/messages、/var/log/secure)。
journald 与 rsyslog 在现代系统中并存——journald 负责收集,然后转发给 rsyslog 归档为文本文件,两套日志系统都在运行。journald 的日志便于通过 journalctl 查询,rsyslog 写入的文本文件便于用传统的 grep/awk 工具处理,也便于被日志采集器(filebeat、promtail)读取。
配置文件
| 路径 | 用途 |
|---|---|
/etc/rsyslog.conf | 主配置 |
/etc/rsyslog.d/*.conf | 附加配置 |
修改后需要重启:
bash
systemctl restart rsyslog规则格式
rsyslog 的规则格式是 facility.level 目标:
conf
*.info;mail.none;authpriv.none;cron.none /var/log/messages
authpriv.* /var/log/secure
cron.* /var/log/cron
*.emerg :omusrmsg:*| 字段 | 含义 |
|---|---|
| facility | 日志来源类别:authpriv(认证)、cron(定时任务)、mail(邮件)、daemon(守护进程)、kern(内核)、local0-7(自定义) |
| level | 记录的最低等级(与 journald 的级别相同) |
| 目标 | 日志写入的文件路径,或转发到远程 |
例如 authpriv.* 表示"认证相关的所有级别日志写入 /var/log/secure",*.info;mail.none 表示"所有 info 及以上级别日志写入 /var/log/messages,但邮件相关的不要"。
应用日志通常不需要动 rsyslog
普通应用服务不需要修改 rsyslog 配置——直接写自己的日志文件即可。只有以下场景才会修改 rsyslog:
- 实现集中日志收集(将日志转发到远程日志服务器)
- 自定义日志路径或日志格式
- 调整系统日志的过滤规则
修改前务必备份,rsyslog 配置错误可能导致日志完全不被记录,排查异常时会陷入"什么日志都没有"的被动局面。
四、dmesg:内核日志
dmesg 查看内核环形缓冲区中的消息,内容包括:
- 系统启动时的硬件探测
- 驱动加载与卸载
- 网络接口状态变化(link up/down)
- 磁盘 I/O 错误
- OOM killer 杀进程的记录
- 内核 panic / oops 信息
排查硬件层、内核层问题时,dmesg 是第一选择。
常用命令
bash
dmesg # 输出所有内核消息(秒数时间戳)
dmesg -T # 时间戳显示为人类可读格式
dmesg -T | tail -n 50 # 最后 50 行
dmesg -T -l err # 只看 error 级别
dmesg -w # 持续监听新消息(类似 tail -f)异常信息过滤
bash
dmesg -T | grep -i -E "error|fail|oom|reset"这条命令快速捕获 dmesg 中的异常信号——磁盘 I/O 错误、网卡 link down、OOM 杀进程、设备复位等。
OOM 事件的交叉验证
dmesg 中可能出现 OOM killer 的记录:
[2026-06-23T10:25:30] Out of memory: Killed process 1234 (java) total-vm:8533124kB, anon-rss:6291456kB
[2026-06-23T10:25:30] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,...发现 OOM 事件后,可以与服务日志做时间线交叉验证:
bash
# 在同一时间窗口查看服务日志
journalctl -u myapp.service --since "2026-06-23 10:25:00" --until "2026-06-23 10:26:00"如果服务日志显示在同一时刻进程异常退出,基本可以确认是系统 OOM 杀掉了进程。这种交叉验证能避免误判——单看应用日志可能以为是应用自己 crash,看了 dmesg 才知道是内核主动杀的。
五、logrotate:日志轮转
日志文件如果不做管理会持续增长,最终撑爆磁盘。logrotate 解决的就是这个问题——它定期把旧日志改名、压缩、删除,保持日志目录的大小在受控范围内。
注意:logrotate 不会减少日志量本身,只是做轮转管理。如果应用日志写得非常频繁,需要先在应用层调整日志级别。
配置文件位置
| 路径 | 用途 |
|---|---|
/etc/logrotate.conf | 全局配置 |
/etc/logrotate.d/* | 各服务的独立配置 |
logrotate 由 cron 触发(/etc/cron.daily/logrotate),每天检查一次。
典型轮转配置
logrotate
/var/log/myapp/*.log {
daily # 每天轮转一次
rotate 7 # 保留最近 7 份
missingok # 文件不存在不报错
notifempty # 空文件不轮转
compress # 压缩旧日志
delaycompress # 延迟压缩(保留最近一份不压缩,方便正在查的内容)
copytruncate # 复制后清空原文件
}关键指令说明:
| 指令 | 含义 |
|---|---|
daily / weekly / monthly | 轮转频率 |
size 100M | 按大小轮转(达到 100M 触发) |
rotate N | 保留 N 份历史 |
missingok | 文件不存在不报错 |
notifempty | 空文件不轮转 |
compress | 压缩旧日志(默认 gzip) |
delaycompress | 延迟一轮压缩,最近一份保持未压缩 |
dateext | 文件名使用日期后缀,而不是数字 |
copytruncate | 复制内容后清空原文件 |
postrotate / endscript | 轮转后执行脚本 |
create 644 user group | 创建新文件并指定权限 |
copytruncate 与 postrotate 的选择
轮转日志时,如何处理"正在写入的进程"是关键问题。两种方案:
方案 1:copytruncate
logrotate
copytruncate工作原理:
- logrotate 把当前日志文件的内容复制到
xxx.log.1 - 清空原
xxx.log文件(truncate 到 0 字节) - 应用进程仍持有原 inode,继续向"已清空"的文件写入
优点:不需要应用支持任何信号或 reload。 缺点:复制和清空之间的瞬间,应用可能写入的少量日志会丢失。
方案 2:postrotate + reload 信号
logrotate
/var/log/nginx/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
postrotate
systemctl reload nginx >/dev/null 2>&1 || true
endscript
}工作原理:
- logrotate 把
xxx.log改名为xxx.log.1 - 执行 postrotate 中的命令,通知应用重新打开日志文件
- 应用 reload 后打开新的同名日志文件
xxx.log,旧文件 inode 已不再被持有
优点:无任何日志丢失。 缺点:需要应用支持 reload(SIGHUP)信号触发重新打开日志文件。
支持 reload 的服务(Nginx、Apache、HAProxy 等)优先使用 postrotate,比 copytruncate 更可靠。
测试 logrotate 配置
修改 logrotate 配置后,先用 -d 模拟运行验证,不要直接生效:
bash
# 第 1 步:debug 模式,只模拟不执行
logrotate -d /etc/logrotate.d/nginx
# 第 2 步:确认输出符合预期后,强制执行一次
logrotate -f /etc/logrotate.d/nginx
# 验证:看是否生成了 .1 后缀的归档文件
ls -lh /var/log/nginx/带 postrotate 和删除动作的配置必须先模拟——logrotate 配置错误可能导致误删日志,且不可恢复。
常见错误配置
错误 1:同一日志文件被多个 logrotate 配置覆盖
bash
grep -l "/var/log/nginx" /etc/logrotate.d/*
# 如果输出多个文件,说明有重复定义,会导致轮转行为不确定错误 2:文件权限错误,logrotate 无法操作
bash
# 确认 logrotate 可写日志目录
ls -ld /var/log/myapp错误 3:compress 和 nocompress 同时存在(后者覆盖前者)
六、日志排查的基本方法
按时间范围定位
时间范围过滤是定位故障最有效的方式——故障一定发生在某个时间点附近:
bash
# journald
journalctl --since "2026-06-23 10:00:00" --until "2026-06-23 11:00:00"
# 文本日志
awk '/2026-06-23 10:00:00/,/2026-06-23 11:00:00/' app.log
sed -n '/2026-06-23 10:00/,/2026-06-23 11:00/p' app.log按关键字过滤
bash
grep -i "error" app.log
grep -i -E "error|exception|failed|timeout" app.log
grep -i -B 5 -A 10 "OutOfMemoryError" app.log # 错误前后 5/10 行的上下文关注日志增长速度
日志的突然暴涨通常是异常信号——某种异常被频繁触发,可能是应用 bug 或外部攻击:
bash
ls -lh /var/log/nginx/access.log # 看单个文件大小
du -sh /var/log/* # 看各日志目录占用
tail -f /var/log/nginx/access.log # 直接观察日志写入速度日志分区独立挂载
一个重要的运维实践:将 /var/log 单独挂载在独立的分区或逻辑卷上。
这样做的价值在于:当应用异常时,可能在短时间内狂写日志(例如某个错误循环每秒产生上千条日志),如果 /var/log 与根分区(/)在同一文件系统:
- 日志写满
/var/log同时也写满了根分区 - 根分区写满后,sshd 等关键服务无法写入临时文件,可能影响登录
- 系统整体陷入半瘫痪状态,排查动作受阻
把 /var/log 独立挂载后,即便日志写满,最多是日志分区被占满,根分区仍有空间,系统其他功能正常运作,登录、查日志、定位问题都不受影响。配置方式:
bash
# 假设新加一块盘 /dev/sdb1 用作日志分区
mkfs.xfs /dev/sdb1
# 添加到 fstab
echo "UUID=$(blkid -s UUID -o value /dev/sdb1) /var/log xfs defaults 0 0" >> /etc/fstab
# 迁移现有日志(需要短暂停服)
systemctl stop rsyslog systemd-journald
mv /var/log /var/log.bak
mkdir /var/log
mount /var/log
cp -a /var/log.bak/* /var/log/
systemctl start rsyslog systemd-journald物理服务器、关键业务节点强烈建议这样规划。云主机通过云盘扩容更便利,但仍然推荐分区独立。