写在前面
日志是排查故障的“黑匣子”,Linux 系统日志的来源有十几种:journald、rsyslog、dmesg、auditd、cron、boot.log 等等。新人最大的困扰是“这些日志都在哪、记录什么、怎么查、查到了怎么读”。
这篇文章把 Linux 系统日志体系彻底拆开讲:
- systemd 自己的日志(
journald + journalctl)。 - 传统 syslog 服务(
rsyslog + /var/log/*)。
读完之后,碰到任何 Linux 故障都能在 10 分钟内翻到对应日志开始定位。
一、Linux 日志体系全景
1.1 数据流图
+-------------------+ +----------------+ +-------------------+| 内核 | --> | rsyslog | --> | /var/log/* || (printk) | | (syslog 协议) | | (文本文件) |+-------------------+ +----------------+ +-------------------+ | | | v v v+-------------------+ +----------------+ +-------------------+| /dev/kmsg | | journald | | logrotate || (内核 ring buf) | | (二进制索引) | | (切割、压缩) |+-------------------+ +----------------+ +-------------------+ | | v v+-------------------+ +----------------+| dmesg | | /var/log/ || (命令读取) | | journal/ |+-------------------+ | (二进制文件) | +----------------+
CentOS 7+ / RHEL 7+ / Ubuntu 16+ 默认是 journald + rsyslog 双栈:
journald 是 systemd 的日志服务,二进制存储在 /var/log/journal/。rsyslog 是传统的 syslog 守护进程,文本存储在 /var/log/。rsyslog 会从 journald 拉取日志再写一份到 /var/log/messages 等文件。
1.2 各日志组件职责
| | | |
|---|
| | | |
| | /var/log/journal/ | |
| | /var/log/*.log | |
| | /var/log/audit/audit.log | |
| | /var/log/<service>/ | |
二、内核日志(dmesg)
2.1 看内核日志
# 看所有内核日志dmesg# 看最近的错误dmesg --level=err,crit,alert,emerg# 看 OOMdmesg | grep -i 'out of memory'# 看网卡dmesg | grep -i eth# 实时跟踪dmesg -w
dmesg 的输出格式:
[ 5.123456] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready[ 10.234567] usb 1-1: new high-speed USB device number 3 using xhci_hcd[ 15.345678] TCP: out of memory -- consider tuning tcp_mem
方括号里是“距系统启动多少秒”,不是时间戳。dmesg -T 加人类可读时间。
2.2 内核日志常见关键字
out of memory:OOM Killer 触发。TCP: out of memory:TCP 内存耗尽。net_ratelimit:网络日志被 rate limit。NIC Link is Up/Down:网卡状态。BUG: scheduling while atomic:内核调度异常。hardware name:hostname 后的硬件名,常用于内核 BUG 报告。
2.3 内核日志清理
dmesg 是 ring buffer,重启会清空。但运行中可以用:
# 清空 dmesg(需要 root)dmesg -C
风险提示:清空 dmesg 后没法回看历史,但不会影响 journald(journald 单独存储内核日志)。
三、journald 与 journalctl
3.1 journald 配置
/etc/systemd/journald.conf:
[Journal]Storage=persistentSystemMaxUse=4GSystemKeepFree=1GSystemMaxFileSize=200MMaxRetentionSec=2monthForwardToSyslog=yesRateLimitIntervalSec=30sRateLimitBurst=1000
关键参数:
Storage=persistent:日志持久化(默认 /var/log/journal/)。Storage=volatile:日志只存内存(重启丢失)。Storage=auto:先尝试持久化,失败转内存。ForwardToSyslog=yes:把日志转发给 rsyslog(默认)。
3.2 journalctl 常用命令
# 看所有日志journalctl# 看内核日志journalctl -k# 看指定服务journalctl -u nginxjournalctl -u nginx --since todayjournalctl -u nginx --since "2026-06-12 09:00" --until "2026-06-12 10:00"# 按时间journalctl --since "1 hour ago"journalctl --since "yesterday"journalctl --since "2026-06-01" --until "2026-06-12"# 按优先级journalctl -p err# 优先级:emerg(0)、alert(1)、crit(2)、err(3)、warning(4)、notice(5)、info(6)、debug(7)# 按 PIDjournalctl _PID=1234# 按 UIDjournalctl _UID=1000# 实时跟踪journalctl -f# 看启动日志journalctl -bjournalctl -b -1 # 上一启动# 看启动过程的日志journalctl --list-boots# 看某可执行文件journalctl /usr/sbin/nginx# 看某设备journalctl /dev/sda# 限制行数journalctl -n 100# JSON 输出journalctl -o jsonjournalctl -o json-pretty# 短格式journalctl -o short
3.3 journalctl 高级用法
# 看哪个服务报错最多journalctl -p err --since today | awk '{print $5}' | sort | uniq -c | sort -rn | head# 看 OOM 事件journalctl -k | grep -i oom# 看某个时间段的所有 sshd 登录journalctl -u sshd --since "2026-06-12 09:00" --until "2026-06-12 10:00"# 合并内核和服务日志journalctl -k -u nginx# 校验日志完整性journalctl --verify# 看磁盘占用journalctl --disk-usage
3.4 journald 日志清理
# 按大小清理journalctl --vacuum-size=1G# 按时间清理journalctl --vacuum-time=1month# 强制保留 1 周journalctl --vacuum-time=1week
风险提示:--vacuum-size 会立即删日志,运行前确认日志不需要保留做分析。
四、rsyslog 与 /var/log/*
4.1 rsyslog 配置
/etc/rsyslog.conf:
# 模块加载module(load="imuxsock")module(load="imjournal")module(load="imklog")# 日志规则模板template(name="TraditionalFormat" type="string" string="%timegenerated% %syslogtag% %msg% %inputname%\n")# 全局规则*.info;mail.none;authpriv.none;cron.none /var/log/messagesauthpriv.* /var/log/securemail.* -/var/log/maillogcron.* /var/log/cron*.emerg :omusrmsg:*uucp,news.crit /var/log/spoolerlocal7.* /var/log/boot.log# 远程日志*.* @10.0.10.10:514
字段含义:
-/var/log/maillog:开头的 - 表示不立即刷盘,性能更好。
4.2 /var/log/* 详解
| | |
|---|
/var/log/messages | | |
/var/log/secure | | |
/var/log/maillog | | |
/var/log/cron | | |
/var/log/spooler | | |
/var/log/boot.log | | |
/var/log/dmesg | | |
/var/log/audit/audit.log | | |
/var/log/yum.log | | |
/var/log/wtmp | | |
/var/log/btmp | | |
/var/log/lastlog | | |
/var/log/tallylog | | |
/var/log/faillock | | |
/var/log/nginx/ | | |
/var/log/httpd/ | | |
/var/log/mysqld.log | | |
/var/log/redis/ | | |
/var/log/tomcat/ | | |
4.3 wtmp / btmp / lastlog
wtmp 是二进制文件,记录所有登录登出:
lastlast -Flast -i # 显示 IP
btmp 是失败登录记录:
lastb
lastlog 是每个用户最近一次登录:
lastlog
4.4 文本日志分析
# 看 secure 日志中的失败登录grep 'Failed password' /var/log/secure | tail -20# 看 sudo 使用grep sudo /var/log/secure# 看 cron 任务grep -i 'cmd' /var/log/cron | tail# 看 yum 安装记录grep 'Installed' /var/log/yum.log# 看 ssh 登录成功grep 'Accepted' /var/log/secure# 看 ssh 异常断开grep -i 'disconnect\|error\|timeout' /var/log/secure
五、各服务的日志位置
5.1 Web 服务
- Nginx:
/var/log/nginx/access.log、/var/log/nginx/error.log。 - Apache HTTPD:
/var/log/httpd/access_log、/var/log/httpd/error_log。 - Tomcat:
/var/log/tomcat/catalina.out、/var/log/tomcat/catalina.YYYY-MM-DD.log、/var/log/tomcat/localhost_access_log.YYYY-MM-DD.txt。 - PHP-FPM:
/var/log/php-fpm/error.log。
5.2 数据库
- MySQL:
/var/log/mysqld.log,慢查询通过 slow_query_log_file 配置。 - Redis:
/var/log/redis/redis.log。 - PostgreSQL:
/var/log/postgresql/。 - MongoDB:
/var/log/mongodb/mongod.log。 - Elasticsearch:
/var/log/elasticsearch/。
5.3 缓存与消息队列
- Memcached:默认不写日志,可以
-vv 启动看连接。 - Kafka:
/var/log/kafka/server.log。 - RabbitMQ:
/var/log/rabbitmq/。 - Nginx stream:
/var/log/nginx/stream.log。
5.4 K8s / Docker
- kubelet:
/var/log/kubelet.log 或者 journalctl -u kubelet。 - kube-apiserver:
/var/log/kube-apiserver.log。 - containerd:
/var/log/containerd/。 - Docker:
/var/log/docker.log。 - Pod 容器:
kubectl logs <pod> -c <container>。
5.5 监控系统
- Prometheus:
/var/log/prometheus/。 - Grafana:
/var/log/grafana/。 - Node Exporter:
/var/log/prometheus/node_exporter.log。 - AlertManager:
/var/log/alertmanager/。
六、日志切割(logrotate)
6.1 logrotate 配置
/etc/logrotate.conf:
# 全局参数dailyrotate 30create 0640 root rootdateextdateformat -%Y%m%dcompressnotifemptymissingoksharedscripts# 包含具体规则include /etc/logrotate.d
/etc/logrotate.d/nginx:
/var/log/nginx/*.log { daily rotate 30 missingok notifempty compress delaycompress sharedscripts postrotate [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid) endscript}
/etc/logrotate.d/tomcat:
/var/log/tomcat/*.log { daily rotate 30 missingok notifempty compress delaycompress copytruncate # 不用 postrotate,copytruncate 截断原文件}
6.2 关键参数
daily / weekly / monthly / yearly:切割频率。delaycompress:下个周期才压缩最新的。create 0640 root root:新建文件权限。copytruncate:复制 + 截断,Nginx/Tomcat 用。postrotate / endscript:切割后执行命令。sharedscripts:所有文件共享一个 postrotate。
6.3 copytruncate vs postrotate
两种方式:
- copytruncate:复制当前文件再清空原文件。简单,但有丢失风险(复制和清空之间有写)。
- postrotate + 信号通知:rename + 创建新文件 + 通知服务 reopen。Nginx 走 USR1 信号。
风险提示:postrotate 里的命令如果写错会导致服务无法继续写日志。/dev/null 2>&1 兜底。
6.4 手动测试
# 强制切割logrotate -vf /etc/logrotate.d/nginx# 调试logrotate -d /etc/logrotate.d/nginx
logrotate -d 是 dry-run,不真切。
6.5 切割后磁盘不释放
现象:logrotate 切割后,业务进程仍然持有旧文件描述符,磁盘没释放。
排查:
lsof | grep deleted# 找到被删除但仍被占用的文件
修复:
- 改用
postrotate 通知服务 reopen(推荐)。 - 改用
copytruncate(不推荐,丢日志风险)。
七、故障案例:从现象到根因
下面 5 个案例都是从“用户报障”开始,按“现象 → 初步判断 → 命令检查 → 关键指标 → 根因定位 → 修复 → 验证 → 复盘”的完整闭环写。
7.1 案例一:SSH 登录失败激增
现象:监控告警“SSH 失败登录 > 100 次/分钟”。
初步判断:SSH 暴力破解。
命令检查:
# 看失败登录 IP 分布grep 'Failed password' /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -rn | head# 看用户名字段grep 'Failed password' /var/log/secure | awk '{print $9}' | sort | uniq -c | sort -rn# 看时间分布grep 'Failed password' /var/log/secure | awk '{print $1, $2, $3}' | uniq -c | tail
关键指标:
- 攻击者尝试多个用户名(
root、admin、test)。
根因:服务器 SSH 端口暴露在公网,被自动化的爆破工具攻击。
修复:
# iptables 封禁iptables -I INPUT -s <attacker_ip> -j DROP# 批量封禁grep 'Failed password' /var/log/secure | awk '{print $11}' | sort -u | xargs -I {} iptables -I INPUT -s {} -j DROP
systemctl enable --now fail2ban
验证:
# 看 fail2ban 状态fail2ban-client status sshd# 看 iptables 封禁数iptables -L INPUT -n | wc -l
复盘:SSH 端口暴露公网是高风险事项,必须通过堡垒机收敛入口。
7.2 案例二:磁盘写满
现象:服务报错“java.io.IOException: No space left on device”。
初步判断:磁盘满了。
命令检查:
# 看磁盘使用率df -h# 看 inode 使用率df -i# 看大文件du -sh /* 2>/dev/null | sort -h | tail# 看具体大目录du -sh /var/log/* | sort -h | taildu -sh /tmp/* | sort -h | taildu -sh /var/lib/docker/ 2>/dev/null# 看大文件find / -type f -size +1G 2>/dev/null | head# 看被删除但仍占空间的文件lsof | grep deleted | head
关键指标:
du 看到 /var/log/messages 单文件 50G。
根因:rsyslog 配置不当,/var/log/messages 持续写入无切割。
修复:
# 立即清理> /var/log/messages# 或truncate -s 0 /var/log/messages# 加 logrotatecat > /etc/logrotate.d/messages <<EOF/var/log/messages { daily rotate 7 size 100M missingok notifempty compress delaycompress sharedscripts postrotate /usr/bin/systemctl kill -s HUP rsyslog.service endscript}EOF
风险提示:> /var/log/messages 是直接清空文件,不影响服务继续写入。比 rm 安全。
验证:
df -hls -la /var/log/messages*
复盘:所有日志必须有 logrotate,必须有磁盘使用率告警(70%、85%、95% 三级)。
7.3 案例三:OOM Killer
现象:服务进程突然消失,监控报“进程不存在”。
初步判断:进程被 OOM Killer 杀掉。
命令检查:
# dmesg 中找 OOMdmesg | grep -i 'out of memory'dmesg | grep -i 'killed process'# journalctl 找 OOMjournalctl -k | grep -i oom
输出形如:
[12345.678901] Out of memory: Killed process 1234 (java) total-vm:8388608kB, anon-rss:4194304kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:8192kB oom_score_adj:0
关键指标:
根因:Java 进程申请内存超过 cgroup limit 或者系统可用内存,触发 OOM。
修复:
# 1. 调整 OOM 优先级(业务进程设成 -100,避免被优先杀)echo -100 > /proc/<pid>/oom_score_adj# 2. 调整 cgroup 内存限制systemctl set-property myapp.service MemoryHigh=4G MemoryMax=6G# 3. 调大系统内存(如果硬件允许)# 4. 优化应用内存使用(减小堆、调优 GC)
验证:
# 看应用启动后是否还会 OOMjournalctl -k | grep -i oom# 看 oom_scorecat /proc/<pid>/oom_scorecat /proc/<pid>/oom_score_adj
复盘:Java 类应用必须监控堆内存使用率,OOM 之前会有预警。
7.4 案例四:网卡丢包
现象:业务反馈“某些用户访问慢,P99 延迟高”。
初步判断:网络问题,可能丢包。
命令检查:
# 看网卡统计ip -s link show eth0# RX errors、dropped、overruns 是关键# 看具体丢包ethtool -S eth0 | grep -i 'drop\|err\|miss'# 看 ring bufferethtool -g eth0# 看网卡驱动状态ethtool eth0# 看 Speed、Duplex、Link detected# 看 dmesg 内核日志dmesg | grep -i ethdmesg | grep -i 'NIC\|net'
关键指标:
- 网卡工作在非协商速率(比如 100Mbps 而不是 1000Mbps)。
根因:网卡 ring buffer 太小,突发流量丢包。
修复:
# 调大 ring bufferethtool -G eth0 rx 4096 tx 4096# 调大 netdev_max_backlogsysctl -w net.core.netdev_max_backlog=300000# 关闭无关的 offloadethtool -K eth0 tso off gso off# 更换万兆网卡(如果是千兆跑满了)
验证:
# 压测iperf3 -c 10.0.0.1 -t 60# 看丢包数ip -s link show eth0
复盘:网卡丢包要从硬件(带宽、协商速率)、驱动(ring buffer、offload)、内核(netdev_max_backlog、busy_poll)三个层面逐项检查。
7.5 案例五:systemd 服务反复退出
现象:服务自动启动后几秒又退出,状态 failed。
初步判断:服务启动失败。
命令检查:
# 看服务状态systemctl status myappsystemctl status myapp -l# 看详细日志journalctl -u myapp -n 100 --no-pagerjournalctl -u myapp --since today# 看启动耗时systemd-analyze blame | grep myapp
关键指标:
Main PID: ... (code=exited, status=...)。systemd[1]: myapp.service: Scheduled restart job, restart counter is at N.
根因:应用启动时连不上数据库 / Redis,启动失败。systemd 默认会自动重启。
修复:
# 1. 修应用(推荐)# 把启动依赖改成先等依赖可用# 2. 调 systemd 重启策略systemctl edit myapp# 添加:# [Service]# Restart=on-failure# RestartSec=10# StartLimitBurst=5# StartLimitIntervalSec=300# 3. 加启动依赖# [Unit]# After=network.target mysql.service redis.service# Requires=mysql.service redis.service
验证:
# 重启服务systemctl restart myapp# 跟踪日志journalctl -u myapp -f
复盘:systemd 服务启动顺序、依赖关系要写清楚,不能依赖自动重启掩盖问题。
八、日志分析工具链
8.1 grep / awk / sed
# grep 高频grep -c 'Failed password' /var/log/securegrep -E 'Failed password|Invalid user' /var/log/securegrep -A 5 -B 5 'Oops' /var/log/messages# awk 提取字段awk '{print $11}' /var/log/secure | sort | uniq -c# sed 流处理sed -n '100,200p' /var/log/messagessed -i 's/old/new/g' /etc/file
8.2 goaccess
实时 Web 日志分析器:
yum install -y goaccess# 实时终端模式goaccess -f /var/log/nginx/access.log -c# 生成 HTML 报表goaccess /var/log/nginx/access.log -o /var/www/report.html -c
8.3 ELK / PLG
前面已经讲过,配置略。
8.4 logwatch
每天生成日志摘要邮件:
yum install -y logwatch
/etc/logwatch/conf/logwatch.conf:
MailTo = ops@example.comDetail = HighService = AllRange = yesterday
8.5 自定义监控脚本
监控 OOM:
#!/bin/bash# /usr/local/bin/oom_monitor.shCOUNT=$(dmesg | grep -c -i 'killed process')if [ "$COUNT" -gt 0 ]; then dmesg | grep -i 'killed process' | tail -5 | mail -s "OOM Alert" ops@example.comfi
监控 SSH 失败:
#!/bin/bashCOUNT=$(grep 'Failed password' /var/log/secure | grep "$(date '+%b %_d')" | wc -l)if [ "$COUNT" -gt 100 ]; thenecho"SSH failed: $COUNT" | mail -s "SSH Alert" ops@example.comfi
九、日志安全与完整性
9.1 日志防篡改
# chattr 锁定日志chattr +a /var/log/messages# +a 是只能 append,不能修改和删除# /var/log 整个目录锁chattr -R +a /var/log/
风险提示:logrotate 切割时 rename 旧文件、create 新文件,+a 会影响切割流程。生产上一般只对核心日志锁。
9.2 日志审计
# 看谁访问了日志auditctl -w /var/log/ -p wa -k log-accessausearch -k log-access
9.3 日志集中收集
- 远程 syslog:rsyslog 客户端发送、rsyslog 中心收集。
- ELK / PLG:Filebeat / Promtail 收集。
# rsyslog 远程*.* @10.0.10.10:514# 加密传输*.* @@(o)10.0.10.10:6514
9.4 入侵后日志清理的识别
# 看日志时间戳跳跃sort -k1,2 /var/log/secure | head# 看日志文件 mtimels -la /var/log/# 看日志空洞(中间缺失)awk '{print $1, $2, $3}' /var/log/secure | uniq -c | head
如果发现日志文件 mtime 异常、内容突然跳到几天前、或者日志量骤减,可能是被入侵者清过。
十、时间同步与日志关联
10.1 时间同步的重要性
日志分析的基础是时间一致。如果各机器时间不同步,故障时间线会乱。
# 看时区timedatectl# 看时间datedate -u# 同步时间chronyc trackingchronyc sources -vtimedatectl set-ntp true
10.2 跨主机日志时间线
多台机器排查时,统一用 UTC 时间:
# 输出 UTCjournalctl --utc# 看时区date +%Z# 在 ELK / Loki 中配置时区
10.3 trace ID 关联
Nginx 的 $request_id 配合 journald 的 _BOOT_ID 能把多台机器的请求串联起来。
Loki / ELK 里用 {job="nginx"} |= "req_id=abc" 跨主机查同一个请求的所有日志。
十一、关键日志的速查命令
# 系统journalctl -xejournalctl -bdmesgcat /var/log/messagescat /var/log/securecat /var/log/cronlastlastb# 服务journalctl -u <service>journalctl -u nginxjournalctl -u mysqld# 性能vmstat 1mpstat 1iostat -x 1ss -s# 网络ss -tulnpnetstat -antcpdump -i eth0 -nn port 80# 文件lsofstrace -p <pid># 资源free -hdf -hdf -i# 进程ps -efpstree -ptophtop
十二、日志保留与归档策略
12.1 保留期限
- 系统日志(messages、secure):90 天。
- 应用日志(Nginx、Tomcat):30~90 天。
12.2 归档到对象存储
#!/bin/bash# 30 天前的日志归档到 OSSfind /var/log/ -name "*.gz" -mtime +30 -exec \ aws s3 cp {} s3://log-archive/$(hostname)/{} \;
12.3 日志清理脚本
#!/bin/bash# /usr/local/bin/log_clean.sh# 清理 30 天前的 .gz 日志,保留 90 天内find /var/log/ -name "*.gz" -mtime +90 -deletefind /var/log/ -name "*.log.[0-9]" -mtime +30 -delete
加入 cron 每天执行。
十三、日志系统的可观测性
13.1 日志采集状态监控
# 看 filebeat 状态systemctl status filebeat# 看 Promtail 状态systemctl status promtail# 看 journald 状态systemctl status systemd-journald# 看 rsyslog 状态systemctl status rsyslog
13.2 关键指标
node_filesystem_avail_bytes:日志磁盘可用空间。journal_entries_total:journald 写入条数。rsyslog_messages_processed_total:rsyslog 处理数。filebeat_events_*:Filebeat 事件数。
13.3 告警规则
groups:-name:logrules:-alert:LogDiskHighexpr:(node_filesystem_avail_bytes{mountpoint="/var/log"}/node_filesystem_size_bytes{mountpoint="/var/log"})<0.15for:5mlabels:severity:warning-alert:JournaldDownexpr:up{job="journald_exporter"}==0for:1mlabels:severity:critical-alert:LogAgentDownexpr:up{job="filebeat"}==0for:1mlabels:severity:warning
十四、常见误区
14.1 “删日志省空间”
短视。删了日志后排查故障没证据,是典型的“省了小钱亏了大钱”。
14.2 “日志越多越好”
不是。日志太多会拖慢性能、撑爆磁盘、隐藏关键信息。要按业务需要分级(DEBUG/INFO/WARN/ERROR)。
14.3 “集中日志一定比本地日志好”
不是。本地日志有 journald 的二进制索引,查询比远程快。集中日志适合长期保存、跨主机分析。
14.4 “logrotate 一定不会出问题”
错。postrotate 写错会丢日志、size 阈值错会导致频繁切割。logrotate 改完先 logrotate -d 调试。
14.5 “时间不同步没关系”
错。日志分析、监控告警、ELK 时间线都依赖时间一致。所有机器必须 chrony 同步,监控时间漂移。
十五、附录:Linux 日志速查表
| |
|---|
| journalctl -xb |
| /var/log/secure |
| /var/log/secure |
| /var/log/secure |
| /var/log/cron |
| dmesg、/var/log/dmesg、journalctl -k |
| dmesg | grep oom、journalctl -k | grep oom |
| dmesg | grep eth |
| df -h |
| journalctl -u <service> |
| /var/log/nginx/error.log |
| /var/log/mysqld.log |
| /var/log/redis/redis.log |
| /var/log/tomcat/catalina.out |
| /var/log/audit/audit.log |
| last、lastb、history、/var/log/secure |
| cat /var/spool/cron/* |
| lsmod |
| find / -perm -u+s |
| cat /var/spool/cron/* |
十六、总结
Linux 日志体系看起来散,但结构很清楚:内核 → journald → rsyslog → 文本日志 → logrotate → 远程。每层都有自己负责的范围,定位问题时要清楚“现在该看哪一层”。
把下面的清单用熟:
- 内核问题:
dmesg、journalctl -k。 - 系统服务:
journalctl -u、/var/log/messages。 - 认证安全:
/var/log/secure、lastb。 - 审计:
/var/log/audit/audit.log、ausearch。
每一次故障排查,建议按下面流程:
- 看时间:异常开始时的系统日志(journalctl --since)。
每一步都有具体命令、具体日志、具体判断标准,照着做就快。这篇文章里的命令、配置、案例、命令速查表都过一遍,你就能在 10 分钟内从“用户报障”查到“根因 + 修复方案 + 验证”。
十七、journald 高级用法
17.1 自定义字段
服务可以在日志中加自定义字段,方便查询:
// C 程序sd_journal_send("MESSAGE=hello world", "PRIORITY=5", "MY_FIELD=custom", NULL);
Python 程序:
from systemd import journaljournal.send("hello world", PRIORITY=5, MY_FIELD="custom")
journalctl 查:
journalctl MY_FIELD=custom
17.2 转发到远程
journald 可以直接把日志转发到远程 syslog:
/etc/systemd/journald.conf:
[Journal]ForwardToSyslog=yesForwardToWall=no
rsyslog 接 journald:
# /etc/rsyslog.d/00-journald.confmodule(load="imuxsock" SysSock.ImJournal="/run/systemd/journal/syslog")
17.3 限制特定服务的日志量
[Journal]RateLimitIntervalSec=30sRateLimitBurst=1000
防止某个 bug 的服务狂打日志把 journald 撑爆。
17.4 看服务的 stdout / stderr
很多服务不写文件,直接打 stdout/stderr,systemd 会自动收集:
journalctl -u myapp -o cat# cat 模式是纯文本,不带元数据
StandardOutput=journal 是 systemd 默认值。
十八、rsyslog 高级用法
18.1 模板
template(name="MyFormat" type="string" string="%timegenerated:::date-rfc3339% %HOSTNAME% %syslogtag%%msg%\n")*.* /var/log/myapp.log;MyFormat
18.2 过滤
# 只记录 nginx 错误:programname, isequal, "nginx" /var/log/nginx-app.log& stop# 屏蔽特定消息:msg, contains, "DEBUG" stop
18.3 队列与缓冲
# 异步发送远程action(type="omfwd" Target="10.0.10.10" Port="514" Protocol="udp" queue.type="LinkedList" queue.size="10000")
队列能避免网络抖动时 rsyslog 阻塞。
18.4 加密传输(RELP)
# 加载 RELP 模块module(load="omrelp")# 发送action(type="omrelp" Target="10.0.10.10" Port="20514")
RELP 走 TCP,能保证不丢消息。
十九、journald 索引机制
19.1 索引字段
journald 内部索引的字段包括:
_COMM、_EXE、_CMDLINE:可执行文件、命令行。_SYSTEMD_UNIT:systemd 单元。_TRANSPORT:日志来源(journal、syslog、stdout、kernel)。
19.2 索引大小
journald 索引文件位于 /var/log/journal/<machine-id>/:
system.journaluser-1000.journal
system.journal 可能有 100M~4G,看保留策略。
19.3 索引损坏
# 校验journalctl --verify# 修复journalctl --repair
修复会丢失部分损坏索引,但能恢复大部分日志。
19.4 清理索引
# 按大小journalctl --vacuum-size=1G# 按时间journalctl --vacuum-time=1month
--vacuum-time 和 --vacuum-size 是不可逆操作,执行前确认。
二十、内核日志深度
20.1 内核 ring buffer 大小
# 看大小cat /proc/sys/kernel/printk_ratelimitcat /proc/sys/kernel/printk_devkmsg# 默认 16KB(`dmesg -c` 后会重置)# 调大(重启失效)dmesg -s 65536
永久调大:
echo'kernel.printk = 4 4 1 7' >> /etc/sysctl.d/99-kernel.conf
printk 4 个值分别对应:控制台、默认、空闲、最小。
20.2 内核日志转储(kdump)
在系统崩溃时把内存 dump 到磁盘:
yum install -y kexec-tools
/etc/kdump.conf:
path /var/crashcore_collector makedumpfile -l --message-level 1 -d 31
/etc/sysconfig/kdump:
KDUMP_KERNELVER=""KDUMP_COMMANDLINE_APPEND="irqpoll maxcpus=1"KDUMP_BACKUP_ACTION="reboot"KDUMP_KEEP_OLD_DUMPS=5
触发 dump:
echo 1 > /proc/sys/kernel/sysrqecho c > /proc/sysrq-trigger
kdump 用于内核 panic / 硬件错误的深度排查。
20.3 内核模块日志
# 看模块加载dmesg | grep -i 'module\|init\|firmware'# 看 USBdmesg | grep -i usb# 看存储dmesg | grep -i 'sd\|nvme\|sata'
二十一、容器环境的日志
21.1 容器内看日志
# dockerdocker logs <container>docker logs --tail 100 -f <container># k8skubectl logs <pod> -c <container>kubectl logs --previous <pod> # 上一个实例kubectl logs -f <pod> --since=1h
21.2 容器日志驱动
docker 日志驱动:
awslogs / gcplogs / azblob:发到云上。
/etc/docker/daemon.json:
{"log-driver": "json-file","log-opts": {"max-size": "100m","max-file": "5" }}
21.3 K8s Pod 日志配置
apiVersion:apps/v1kind:Deploymentmetadata:name:myappspec:template:spec:containers:-name:myappimage:myapp:1.0env:-name:LOG_LEVELvalue:"INFO"# 日志输出到 stdout/stderr,由 kubelet 收集
Pod 内的应用必须打 stdout / stderr,不要写文件。
21.4 K8s 日志收集
Fluent Bit / Vector / Promtail 部署为 DaemonSet,收集节点上所有容器日志。
fluent-bit-config.yaml:
apiVersion:v1kind:ConfigMapmetadata:name:fluent-bit-configdata:fluent-bit.conf:| [SERVICE] Flush 5 Daemon off Log_Level info[INPUT]NametailPath/var/log/containers/*.logParserdockerTagkube.*[OUTPUT]NamelokiMatch*HostlokiPort3100
二十二、systemd-journald 远程
journald 支持把日志远程发送:
/etc/systemd/journald.conf:
[Journal]# 启用远程接收[Receive]# 接收端的设置
但实际上很少用 journald 远程,一般都走 rsyslog / Filebeat / Promtail。
二十三、日志系统性能调优
23.1 journald 性能
- 调大
RateLimitBurst:默认值 1000,高并发服务可能不够。 - 用
Storage=volatile:临时机器不写盘,性能最好。 - 关闭
ForwardToSyslog:减轻 rsyslog 压力(要权衡)。
23.2 rsyslog 性能
action(type="omfile" file="/var/log/big.log" asyncWriting="on")
- 关闭
RepeatedMsgReduction 减少重复消息的写入。
23.3 filebeat / promtail 性能
- 调大
harvester_buffer_size。 - 用
multiline 合并多行(Java stack trace)。 - 用
tail_files: true 只跟踪新增。
二十四、故障排查工作流模板
把排查流程做成可复用的脚本:
#!/bin/bash# /usr/local/bin/fault_check.shHOST=$1echo"===== $(date) ====="echo"=== 主机: $HOST ==="echo"[1] 内存"free -hechoecho"[2] 磁盘"df -hechoecho"[3] CPU 负载"uptimeechoecho"[4] 网络"ss -sechoecho"[5] 最近的 OOM"dmesg | grep -i 'oom\|killed' | tail -5echoecho"[6] 失败的 SSH 登录"grep 'Failed password' /var/log/secure | tail -5echoecho"[7] 失败的 systemd 服务"systemctl --failedechoecho"[8] 最近的错误日志"journalctl -p err --since "1 hour ago" | tail -10
执行:
ssh $HOST'/usr/local/bin/fault_check.sh'
一次性拿到所有关键指标。
二十五、与告警系统联动
把日志告警接入 Prometheus AlertManager 或者集中告警平台:
#!/bin/bash# /usr/local/bin/log_alert.sh# 关键词告警KEYWORDS=("Out of memory""disk full""panic""kernel: BUG""Too many open files""no space left")LOG_FILE=/var/log/messagesfor kw in"${KEYWORDS[@]}"; do count=$(grep -c "$kw"$LOG_FILE)if [ "$count" -gt 0 ]; thenecho"$kw: $count occurrences" | mail -s "Log Alert: $kw" ops@example.comfidone
加入 cron:
* * * * * /usr/local/bin/log_alert.sh
二十六、入侵后日志取证
26.1 取证清单
发现入侵后立即做的事:
# 1. 取网络证据ss -antp > /tmp/ss.txtnetstat -anp > /tmp/netstat.txt# 2. 取进程证据ps auxf > /tmp/ps.txtpstree -p > /tmp/pstree.txt# 3. 取登录证据w > /tmp/w.txtlast > /tmp/last.txtlastb > /tmp/lastb.txt# 4. 取历史证据cat /root/.bash_history > /tmp/history.txt# 5. 取计划任务crontab -l > /tmp/crontab.txtls -la /etc/cron.* > /tmp/cron.txt# 6. 取内核模块lsmod > /tmp/lsmod.txt# 7. 取 SUID 文件find / -perm -u+s -type f > /tmp/suid.txt# 8. 取内存cat /proc/meminfo > /tmp/meminfo.txt# 9. 取所有日志cp -r /var/log /tmp/log_backup/# 10. 打包tar -czf /tmp/evidence.tar.gz /tmp/*.txt /tmp/log_backup/
26.2 时间线分析
用 log2timeline 工具生成时间线:
pip install plaso# 生成时间线log2timeline.py --storage-file timeline.plaso /var/log# 导出psort.py -o l2tcsv timeline.plaso > timeline.csv
时间线里能看到每个事件的具体时间点。
26.3 文件分析
# 看被修改的文件find /etc -mtime -7# 看新增的 SUIDfind / -perm -u+s -mtime -7# 看新增的可执行文件find / -mtime -7 -executable -type f
二十七、日志聚合架构
27.1 典型架构
+----------------+ +----------------+ +----------------+| 业务机 | | 业务机 | | 业务机 || rsyslog/filebeat| | rsyslog/filebeat| | rsyslog/filebeat|+-------+--------+ +-------+--------+ +-------+--------+ | | | +----------+------------+-----------+-----------+ | | v v +------+------+ +-------+-------+ | Kafka | | ELK / Loki | +------+------+ +-------+-------+ | | v v +------+------+ +-------+-------+ | 实时告警 | | 长期存储、可视化| | (Flink/Spark)| | (Grafana) | +-------------+ +---------------+
27.2 Kafka 作为缓冲
业务日志先发到 Kafka,下游多个消费者(告警、归档、分析)独立消费。
优点:
27.3 实时流处理
Flink / Spark Streaming 实时分析日志流:
二十八、日志系统的可用性保障
28.1 日志服务自身的高可用
journald / rsyslog 都是单机组件。保证可用性:
- 用 systemd 守护(
Restart=on-failure)。 - 监控
systemctl status systemd-journald。
28.2 日志不丢
- journald:
Storage=persistent 写盘。
28.3 日志查询的可用性
二十九、日志查询模板
把常见查询写成 Grafana 面板或 Kibana Saved Search:
# 错误率sum(rate(log_messages{level=~"err|crit"}[5m]))by(service)# Top 错误topk(10,count_over_time({level=~"err"}[1h]))# OOM{job="system",msg=~".*outofmemory.*"}# 登录失败{job="secure",msg=~"Failedpassword"}
三十、日志系统的元数据
每条日志应该带足够元数据,方便查询:
env:环境(dev/staging/prod)。
元数据越全,跨服务、跨主机的查询越方便。
三十一、写日志的最佳实践
31.1 写什么
31.2 不写什么
31.3 结构化日志
用 JSON 格式:
{"time": "2026-06-12T10:00:00Z","level": "INFO","service": "myapp","host": "web-01","trace_id": "abc123","user_id": 12345,"msg": "user login","duration_ms": 12}
ELK / Loki / Splunk 都支持 JSON 解析。
31.4 异步日志
Java 用 Logback 的 AsyncAppender,避免日志阻塞业务线程:
<appendername="ASYNC"class="ch.qos.logback.classic.AsyncAppender"><queueSize>2048</queueSize><discardingThreshold>0</discardingThreshold><neverBlock>true</neverBlock><appender-refref="FILE" /></appender>
风险提示:异步日志可能在进程崩溃时丢失部分日志。neverBlock=true 会丢弃新日志,避免业务阻塞。
三十二、附录:常用 journalctl 命令
# 启动日志journalctl -bjournalctl --list-boots# 跟踪journalctl -f# 服务journalctl -u nginx.servicejournalctl -u 'nginx*'# 优先级journalctl -p err -bjournalctl -p info# 时间journalctl --since "1 hour ago"journalctl --since "2026-06-12 09:00" --until "2026-06-12 10:00"# 内核journalctl -k# 进程journalctl _PID=1234journalctl _UID=1000journalctl /usr/sbin/sshd# 设备journalctl /dev/sda# 限制journalctl -n 100# 格式journalctl -o shortjournalctl -o jsonjournalctl -o json-prettyjournalctl -o cat# 维护journalctl --verifyjournalctl --disk-usagejournalctl --vacuum-size=1Gjournalctl --vacuum-time=1monthjournalctl --rotate# 看某次启动journalctl -b -1
三十三、附录:常用 rsyslog 命令
# 看 rsyslog 状态systemctl status rsyslog# 看 rsyslog 进程ps aux | grep rsyslog# 看配置文件rsyslogd -f /etc/rsyslog.conf -N 1# -N 1 是验证配置# 看错误journalctl -u rsyslog# 重启systemctl restart rsyslog# 重新加载(不丢现有连接)systemctl reload rsyslog# 看网络接收ss -unlp | grep 514ss -tlnp | grep 514
三十四、附录:常用 dmesg 命令
# 看全部dmesg# 人类可读时间dmesg -T# 实时跟踪dmesg -w# 优先级过滤dmesg --level=errdmesg --level=err,crit,alert,emerg# 设施过滤dmesg --facility=kern# 解码数字dmesg -x# 清空dmesg -C
三十五、致谢与下一步
日志系统是个“一旦搭好就忘了它存在”的组件,但出问题时是排查的核心。这篇文章把 Linux 系统日志体系从内核、journald、rsyslog、logrotate、容器日志、日志分析工具、案例分析、可观测性都覆盖了。
下一步建议:
- 部署 Loki + Promtail 做轻量集中日志。
日志系统不是一蹴而就,是持续运营的产物。生产上一旦发现定位慢了,立刻去看是哪个环节在掉链子:是不是 logrotate 没切、journald 索引太大、rsyslog 阻塞、还是 ELK 索引设计不合理。逐个优化。