Skip to content

日志管理

系统排障基本都靠日志。Linux 上的日志来源主要有三类:systemd journald(服务日志,现代系统主流)、rsyslog(传统系统日志)、应用自己写的文件日志(比如 Nginx 的 access.log)。这三类日志位置不同、查看方式不同,排查问题的时候要知道去哪找。再加上日志轮转(logrotate)防止日志把磁盘撑爆——这套组合在生产里几乎是标配。

一、日志目录和文件

大多数系统和服务的日志都集中在 /var/log 下,但不同发行版文件名不一样,排查前先确认目标机器是哪种发行版:

文件哪种发行版装什么
/var/log/messagesRHEL 系通用系统日志
/var/log/syslogDebian/Ubuntu通用系统日志
/var/log/secureRHEL 系认证和安全相关(SSH 登录、sudo)
/var/log/auth.logDebian/Ubuntu认证和安全相关
/var/log/dmesg通用内核启动日志快照
/var/log/cron部分发行版cron 执行日志

遇到登录相关问题(SSH 登不上、sudo 失败)就看 secureauth.log;内核和硬件问题(磁盘报错、网卡 down、OOM)看 dmesg。记一下这个对应关系,排查时省得满世界找。

二、journald 和 journalctl

systemd 自带的日志系统叫 journald,它收拢了 systemd 管理服务的 stdout/stderr、内核日志、还有部分系统日志,统一存在二进制格式的日志文件里(不是纯文本)。查询得用 journalctl,不能直接 cat:

bash
journalctl                        # 查看全部日志(通常非常多,别直接这么用)
journalctl -u nginx               # 只看指定 unit 的日志
journalctl -u nginx -f            # 持续跟踪,类似 tail -f
journalctl -u nginx --since "1 hour ago"
journalctl -p err                 # 只看 error 级别及以上的日志

几个高频参数:-u 过滤指定 systemd unit、-f 持续输出新日志、--since/--until 按时间过滤(支持 "today"、"1 hour ago"、"2026-05-21" 这种写法)、-p 按日志级别过滤、-n 显示最近 N 行、--no-pager 不分页直接输出(脚本处理或者重定向时必备)。

服务启动失败的标准排查套路:

bash
systemctl status app --no-pager           # 看状态和最后几行日志
journalctl -u app -n 100 --no-pager       # 看最近 100 行

从最近的日志开始看,再按需要往回扩大时间范围。别一上来就翻全量日志——一个跑了半年的服务,全量日志可能几个 G,从头翻根本看不过来。

journald 有个持久化的坑:默认情况下,如果 /var/log/journal 目录不存在,journald 只把日志存在 /run/log/journal(这是 tmpfs,在内存里),重启之后日志全没了。排查"重启前的日志找不到"这种问题,就是这个原因。要持久化:

bash
mkdir -p /var/log/journal
systemctl restart systemd-journald

建好目录、重启 journald 之后,日志就会落到磁盘上,重启不丢。

三、rsyslog

rsyslog 是传统的系统日志服务,负责按规则把不同来源的日志写到对应的文本文件里(就是 /var/log/messages/var/log/secure 这些)。journald 和 rsyslog 可以共存——现代系统里,journald 负责收集,再转发给 rsyslog 归档成文本文件,两套都在跑。

配置文件在 /etc/rsyslog.conf(主配置)和 /etc/rsyslog.d/(附加配置)下,改完要重启:

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

facility 是日志来源类别(authpriv=认证、cron=定时任务、mail=邮件、daemon=守护进程),level 是记录的最低等级。authpriv.* 意思就是认证相关的所有级别日志都写到 /var/log/secure

一般应用服务不需要动 rsyslog 配置。只有要做集中日志收集、自定义日志路径、或者调整系统日志行为的时候才改它。改之前一定备份,rsyslog 配错可能导致日志不记录或者系统异常。

四、dmesg 内核日志

dmesg 查看内核环形缓冲区里的消息,内容覆盖硬件探测、驱动加载、OOM killer、网络接口状态这些。排查硬件和内核层面的问题,dmesg 是第一选择:

bash
dmesg
dmesg -T                     # 显示人类可读的时间戳(不加 -T 是秒数)
dmesg -T | tail -n 50

过滤异常信息:

bash
dmesg -T | grep -i -E "error|fail|oom|reset"

磁盘 I/O 错误、网卡 link down、OOM 杀进程、USB 设备异常,这些都能在 dmesg 里找到痕迹。有个排查思路:dmesg 里看到 Out of memory: Killed process 1234 (java),再用 journalctl -u app.service --since "10 minutes ago" 看同一时间服务是不是退出了——两边对得上,基本能确认是系统 OOM 把进程杀了;如果两边时间差了好几个小时,事故时间线要以 journald 和应用日志里的真实时间为准。这种交叉验证能避免误判。

五、logrotate 日志轮转

日志文件不管会一直增长,迟早把磁盘撑爆。logrotate 解决的就是这个问题——它定期把旧日志改名、压缩、删除,保持日志目录不会无限变大。注意它不是减少日志量,只是做轮转管理。

配置文件在 /etc/logrotate.conf(全局)和 /etc/logrotate.d/(各服务的独立配置):

bash
cat /etc/logrotate.conf              # 全局配置
ls /etc/logrotate.d/                 # 各服务的独立配置

一个典型的轮转配置:

logrotate
/var/log/myapp/*.log {
    daily                    # 每天轮转一次
    rotate 7                 # 保留最近 7 份
    missingok               # 文件不存在不报错
    notifempty              # 空文件不轮转
    compress                # 压缩旧日志
    delaycompress           # 延迟压缩(保留最近一份不压缩,方便还在查)
    copytruncate            # 拷贝后清空原文件
}

这里有个选择:copytruncate 还是 postrotatecopytruncate 是先把日志内容复制到轮转文件,再清空原文件——优点是不需要服务支持重新打开日志文件,缺点是复制和清空之间那一瞬间的少量日志可能丢。

对于支持 reload 信号的服务(比如 Nginx),更可靠的方式是 postrotate:

logrotate
/var/log/nginx/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    postrotate
        systemctl reload nginx >/dev/null 2>&1 || true
    endscript
}

轮转完成之后发 reload 信号让服务重新打开日志文件——服务用的是新的空文件,旧的改名归档,全程不丢日志。支持 reload 的服务优先用 postrotate,比 copytruncate 可靠

测试配置,正式执行前先模拟:

bash
logrotate -d /etc/logrotate.conf      # -d 是 debug 模式,只模拟不执行
logrotate -f /etc/logrotate.conf      # -f 强制执行一次

强制轮转之前一定先用 -d 模拟跑一遍,特别是带 postrotate 和删除动作的配置,确认行为符合预期再真跑。logrotate 配错可能误删日志,这种事恢复不回来。

六、日志排查的基本方法

按时间范围定位,这是最常用的:

bash
journalctl --since "2026-05-21 10:00:00" --until "2026-05-21 11:00:00"

按关键字过滤:

bash
grep -i "error" app.log
grep -i -E "error|exception|failed|timeout" app.log

关注日志增长速度——日志突然暴涨往往是异常信号:

bash
ls -lh app.log              # 单个文件大小
du -sh /var/log/*           # 各日志目录占用
tail -f app.log             # 观察写入速度

一个实用的防护措施:把 /var/log 单独挂一个分区或者逻辑卷。这样应用异常狂写日志的时候,顶多把日志分区写满,不会影响根分区,系统本身还能正常运行、还能登录排查。如果日志和系统共用根分区,日志写满根分区,整个系统直接卡死,连登录都难——这种事故处理起来非常被动。