1. 日志在哪里:Linux日志体系速览
干运维这些年,我见过太多同事一接到“服务挂了”“磁盘满了”“被入侵了”的告警,第一反应是先去翻代码、重启服务,折腾半天才想起来看日志。其实 Linux 系统早已把日志体系搭得明明白白,只要你搞清楚日志文件放在哪、由谁在写、怎么轮转,排障效率能提升一大截。
先说最常见的一类日志文件,多数都在 /var/log 目录下面。这个目录是 Linux 系统日志的默认归宿,不管是 Debian/Ubuntu 系还是 CentOS/RHEL 系,核心日志基本都在这里。常用的几个文件我给你列一下:
/var/log/messages:RHEL/CentOS 系的主日志文件,记录了系统启动、服务启停、内核消息、认证失败等大部分事件。/var/log/syslog:Debian/Ubuntu 系的主日志文件,作用和 messages 一样,也是“万能日志桶”。/var/log/secure:安全相关日志,记录 SSH 登录、sudo 提权、用户切换等关键操作。被暴力破解时翻这个文件比什么都直观。/var/log/auth.log:Ubuntu/Debian 系的安全日志,对应 secure。/var/log/dmesg:内核环缓冲区日志,记录硬件识别、驱动加载、启动报错等。设备不识别、网卡搞不上时,第一条就查它。/var/log/cron:计划任务执行日志,crontab 里任务没跑、执行报错,基本都在这。/var/log/boot.log:系统启动过程日志,排查开机卡死、服务启动失败时有用。
有一个很容易踩的坑是 /var/log/messages 和 /var/log/syslog 并不是同一个东西,底层都是 rsyslog 或 syslog-ng 在写,但不同发行版把它们拆分到了不同文件名。你在一台 Ubuntu 服务器上去 tail messages,大概率什么都没有,因为 Ubuntu 默认写的是 syslog。
除了这些系统级日志,各类服务和应用还有自己的专属日志,比如 Nginx 写在 /var/log/nginx/access.log 和 error.log,MySQL 写在 /var/log/mysql/error.log,Java 应用如果没做日志重定向,一般都在应用启动脚本指定的目录里。这个逻辑不复杂,但刚接触 Linux 的人最容易犯的错,就是一股脑扎进 /var/log 翻找,却忘了查应用自己的日志目录。
这里我再提一个现代 Linux 系统绕不开的话题:systemd。当前几乎所有的主流发行版都用 systemd 管理服务,而 systemd 会接管服务的标准输出和错误输出,统一存到 journald 里去,对应命令 journalctl。也就是说,一个服务即使你自己不写任何日志文件,只要它在 stdout 上打了内容,journalctl -u 服务名 就一定能看到。这其实是排查问题时最不该漏掉的入口。
一句话总结日志体系两层结构:传统文本日志(/var/log 下按文件管理)加 journald 二进制日志(journalctl 查询)。前者适合文件级处理、日志采集,后者适合快速按服务、按时间过滤排查。理解了这个大框架,接下来谈各种查看命令才有底。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 查看日志的核心命令实战
2.1 最常用的三个命令:tail、grep、less
看日志离不开这三个命令,但每个命令都有自己的打开方式,不能乱用。
tail 是我日常敲得最多的命令,没有之一。默认显示文件的最后 10 行,这个用法试日志最省事。但真正进入生产排障模式后,重点其实在 -f 参数上,tail -f 会持续跟踪文件输出,新写入的日志会实时滚动出来,非常适合观察服务启动过程、查看实时请求量。
我先给几个高频组合:
bash复制# 查看最后 100 行日志
tail -n 100 /var/log/syslog
# 持续跟踪日志输出
tail -f /var/log/nginx/access.log
# 跟踪时过滤关键字
tail -f /var/log/syslog | grep "ERROR"
# 查看最后 500 行并退出,适合排查大量报错时的快照
tail -n 500 /var/log/messages
这里有个细节很关键:tail -f 适合跟踪,但如果你把整个历史日志从头 cat 出来,那就灾难了。日志文件动辄几个 G,cat 一遍直接刷屏,还会让 SSH 会话卡屏,正确姿势是先用 wc -l 看下文件行数,再用 tail -n 取尾部一段。
grep 的价值是过滤。日志文件动辄几千行,直接看会看瞎眼,用关键字收缩范围是基本操作。比如排查 Nginx 的 500 错误:
bash复制grep " 500 " /var/log/nginx/access.log
但 grep 的本事远不止一个关键字搜索,常见的几个有用参数:
bash复制# 带行号输出,定位更精准
grep -n "OutOfMemoryError" /var/log/app.log
# 忽略大小写,日志里 ERROR 和 error 都要捞出来
grep -i "error" /var/log/syslog
# 只输出匹配的文件名,多个日志一起搜时很好用
grep -l "panic" /var/log/*.log
# 用正则做复杂匹配,比如匹配所有 5xx 状态码
grep -E "HTTP/1.1\" [5][0-9][0-9]" /var/log/nginx/access.log
不过真在超大文件上做筛选,grep 有更好的搭档——less。很多人不知道,less 不仅是个分页器,它还支持直接打开大文件后进入搜索模式,翻页和搜索可以无缝切换。我的习惯是先用 less 打开日志,然后 / 键输入关键字搜索,按 n 跳到下一个匹配项,按 N 回到上一个,配合 G 跳到最后一行,比反复执行 grep 和 tail 舒服得多。
bash复制less /var/log/syslog
# 在 less 界面内:/ERROR 按 n 下一个匹配,按 N 上一个
2.2 journalctl:systemd 时代的日志查看方式
再说 journalctl。现在写服务排障的文章,如果不提 journalctl,基本等于没跟上时代。几乎所有 systemd 托管的服务,stdout 和 stderr 都会被 journald 收取,这就意味着你不一定总能找到那个服务的日志文件,但用 journalctl 一定看得到它的输出。
常用组合和维护场景:
bash复制# 查看某个服务的完整日志
journalctl -u nginx.service
# 查看最近 30 分钟的日志
journalctl --since "30 min ago"
# 查看某段时间范围内的日志
journalctl --since "2025-01-10 08:00:00" --until "2025-01-10 12:00:00"
# 只查 error 级别及以上的日志
journalctl -p err
# 带分页实时跟踪,和 tail -f 效果对齐
journalctl -f
实际用下来,我最常用的两个参数是 -u 和 -p。一个定位服务,一个过滤级别。比如服务起不来,我一般这么干:
bash复制journalctl -u myservice.service -n 200 --no-pager
解释一下:-n 200 限制输出最近 200 行,--no-pager 直接全量输出到屏幕,因为 journalctl 默认调 less 分页,在脚本环境或快速排障时会卡住,加上 no-pager 一步到位。
journald 的日志还带优先级字段,从 0(emerg)到 7(debug),-p err 等价于只显示 0~3 级的内容。注意这个参数是“及以下级别”的意思,不是只显示精确匹配的级别。所以想看 warning 及以上,用 -p warning 就行。
2.3 日志查看的直观性问题:用什么可视化方案
热搜里有一条“查看日志怎么能直观呢”,这其实是所有运维都会遇到的困惑。纯黑底白字的终端看日志,眼球受罪不说,还容易漏信息。我的经验是分三个层次解决这个问题。
第一层,终端本地的配色方案。像 tail、less、grep 本身都是纯文本输出,但在 .bashrc 里给 grep 加上 --color=auto,匹配关键字就有高亮。进一步可以装一个 grc(Generic Colouriser),它会根据日志文件的正则规则自动给不同级别上色,ERROR 红色、WARNING 黄色、INFO 绿色,一眼扫过去就能抓重点。grcon 对支持的日志文件格式,直接用 grc tail -f /var/log/syslog 就能看到彩色输出,实测体验提升明显。
第二层,日志分析工具的终端 UI 方案。比如 lnav,它不只是高亮,还能自动解析常见日志格式,识别时间戳、日志级别、IP 地址,按时间线排列,支持按错误聚合统计,按快捷键 e 查看错误列表,i 查看日志摘要,t 按时间线浏览。这个工具我用了挺久,对 Nginx access log、syslog 这类结构化程度较高的日志效果非常好。安装也简单,主流发行版仓库里都有。唯一需要注意的是,lnav 对超大日志(10G 以上)加载会慢,需要在投入生产前先小范围试一下。
第三层,把日志往集中管理平台送。这就是另一个话题了,后面专门展开。本地查看永远只是第一个动作,真正的大规模排障还得靠集中日志平台,比如 ELK、Loki、ClickHouse 等,但很多中小团队没有搭这套东西,那么 lnav 和 grc 已经能救急。
3. 日志分析:从查看到定位问题
3.1 时间筛选的常用姿势
日志排障的第一个关键动作,是锁定时间范围。日志量巨大,没有时间范围就盲目搜索,等于大海捞针。我一般按下面的顺序来:
先通过监控或用户反馈拿到大概时间点,然后用 grep 配合时间字符串去过滤。日志里每行基本都带时间戳,比如 Jan 10 08:00:01,就可以直接:
bash复制grep "Jan 10 08:00" /var/log/syslog
但这样写有一个问题:如果日志是按小时写入的,时间写法和系统 locale 可能不一致,甚至年份不同(syslog 默认不记录年份)。所以更稳的做法是结合 sed 截取行号区间。
bash复制# 找到 08:00 出现的第一行行号
grep -n "^Jan 10 08:00" /var/log/syslog | head -1
# 找到 08:05 出现的第一行行号
grep -n "^Jan 10 08:05" /var/log/syslog | head -1
# 用 sed 截取中间行,比如 12345 到 12890 行
sed -n '12345,12890p' /var/log/syslog
这个组合拳在处理超过 1GB 的巨型日志时尤其有用。日志文件有时候大得离谱,直接 less 打开也会卡,先 grep 行号再 sed 截断,是最省内存的操作方式。另外在带时间段的场景下,journalctl --since --until 对 systemd 日志是一种非常优雅的替代方案,完全不需要手动算行号。
3.2 关键词过滤与链路跟踪技巧
日志分析最核心的功夫,其实在“怎么选关键词”上。很多新人上来就 grep “error”,结果日志里到处都是第三方库报的 error,真正的业务异常被淹没了。我的经验是分维度下关键词:
- 错误级别词:
ERROR、Exception、FATAL、panic、failed to。 - 业务关键字:比如订单系统的
order_id、支付系统的transaction_id、登录服务的login failed。 - 组件特征词:比如 Redis 的连接超时
timeout、数据库的 deadlock、Nginx 的upstream timed out。
用多个关键词组合过滤,能非常快地缩小范围。比如排查一个“某时段下单报错”的问题,我可能同时会搜 ERROR、order_id、timeout 三个词,看它们是否出现在同一时间窗口。如果三者集中出现,那基本锁定是远程接口超时导致的下单异常。
进阶一点的技巧是用 grep -A 和 -B 看上下文。Java 异常堆栈会跨多行输出,单独搜一个关键词只能看到异常摘要,看不到栈轨迹:
bash复制# 显示匹配到 Exception 的行,以及之后 20 行
grep -A 20 "Exception" /var/log/app.log
# 显示匹配行之前 5 行、之后 10 行
grep -B 5 -A 10 "NullPointerException" /var/log/app.log
还有一个很实用的场景是跟踪请求 ID。现在主流微服务架构里,网关会为每个请求生成一个 trace_id 或 request_id,这个 ID 会贯穿所有服务的日志。排查链路问题时,只要找到出问题的请求 ID,然后全局搜这个 ID,就能把整条链路的日志串起来:
bash复制grep "trace_id=8f3a12c9e7b65d" /var/log/nginx/access.log /var/log/app/*.log
这个方法比按时间猜位置靠谱得多,尤其是多节点部署的时候。
3.3 日志统计与 top 分析
除了查异常,日志还有一个重要使命:做统计分析。比如快速统计某个 error 出现的次数、某个 IP 的请求量、某接口 5xx 的比例,这些都离不开 sort、uniq、awk 的组合。
看一个最经典的场景:统计访问日志中出现次数最多的 10 个 IP。
bash复制awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
解释一下:awk 提取每行的第一列(IP),sort 把相同 IP 排在一起,uniq -c 统计每个 IP 出现次数,sort -rn 按数字降序排列,head -10 取前 10 名。
如果想把 HTTP 状态码分布统计出来,可以用类似逻辑:
bash复制grep -oE 'HTTP/1.1" [0-9]{3}' /var/log/nginx/access.log | awk '{print $2}' | sort | uniq -c | sort -rn
这里 grep -oE 提取出状态码相关的部分,awk 取第二列,也就是三位状态码本身,然后排序统计。排查“用户报障说系统很卡”的时候,先看 5xx 的数量有没有突增,再看 4xx 是不是被刷接口,这套操作基本能给出大致方向。
更细粒度的性能分析场景,比如统计某些接口的平均响应时间,就需要 awk 做数值运算了。假设 Nginx access log 的最后一列是 $request_time,可以这样写:
bash复制# 提取某个接口的响应时间,求和、计数、算平均
grep "/api/order" /var/log/nginx/access.log | awk '{sum+=$NF; count++} END {printf "avg=%.3f count=%d\n", sum/count, count}'
$NF 是 awk 内建变量,表示当前行的最后一个字段。需要注意不同 Nginx log_format 配置不一样,字段顺序不同,不能直接照抄,必须要先 tail -1 看一眼日志实际格式再定字段下标。
4. 日志管理:轮转、文件清理与采集
4.1 logrotate 机制与配置
日志不能无限增长,否则最终会撑爆磁盘。Linux 下的标准解决方案是 logrotate,一个按周期自动轮转、压缩、删除旧日志的工具。它由 cron 驱动,每天定时执行一次,读取 /etc/logrotate.conf 和 /etc/logrotate.d/ 下所有配置文件。
最常见的配置大概是这样的:
code复制/var/log/nginx/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
拆开讲一下每个配置项的作用:
daily:每天轮转一次。也可以写成 weekly、monthly,按日志量决定。流量大的应用建议 daily,流量小的 weekly 就够。rotate 7:保留 7 个旧日志文件,超过 7 个就删掉最老的。这个数字乘以单文件大小约等于日志占用空间的上限。compress:旧的日志文件需要压缩成 .gz,节省磁盘空间。delaycompress:延迟压缩,会跳过最近一个轮转出来的日志文件不压缩。为什么要有这个参数?因为有些程序还没完全释放日志文件句柄,立即压缩可能导致内容丢失。missingok:日志文件不存在时忽略报错,避免 cron 每天往邮箱发错误信息。notifempty:日志文件为空时跳过轮转。create 0640 nginx adm:轮转后自动创建新的空日志文件,指定权限、属主、属组。sharedscripts和postrotate:轮转完成后执行一段脚本。这里对 Nginx 是发送 USR1 信号让它重新打开日志文件。
postrotate 是 logrotate 配置里最容易出问题的点。很多服务并不会自动重新打开日志文件,需要给它一个信号或者重载配置,如果不处理,服务会继续往已经被重命名的旧文件里写内容,导致“轮转了但没效果”的假象。Nginx 用 kill -USR1,Apache 用 graceful 重载,rsyslog 一般用 kill -HUP。不同的服务信号不一样,务必要查一下对应软件的官方文档。
我没有给 spring boot 这类 Java 应用配置过 logrotate,因为多数 Java 应用自己就用 logback 或 log4j2 做了滚动策略。但如果一个 Java 应用直接把日志输出到某个固定文件没有滚动,则 logrotate 也能兜底,唯一需要注意的是 postrotate 里通常需要触发应用的日志句柄重开,对 Java 应用来说往往做不到,所以要么用系统的 copytruncate 参数,要么干脆靠应用内部的滚动机制。
copytruncate 是替代方案,它先把现有日志文件复制一份,再截断原文件。应用完全无感知,不需要重开文件句柄。缺点是复制了一整个大文件,I/O 开销高,但胜在兼容性极好。我一般在托管型 Java 应用日志上就配这个参数:
code复制/var/log/myapp/app.log {
daily
rotate 14
compress
copytruncate
}
4.2 日志文件占满磁盘的紧急救援
日志撑爆磁盘是运维的“宿命时刻”,我处理过太多次了。场景基本一致:某个服务的日志没配轮转,或者轮转配置有误,导致 /var 分区 100% 占满,数据库、Web 服务一起崩溃。
紧急处理的正确顺序是:
bash复制# 第一步:查看磁盘占用情况
df -h
# 第二步:找出 /var 下最大的文件
du -ah /var/log 2>/dev/null | sort -rh | head -20
du 扫描整个 /var/log 目录,sort -rh 按人类可读的数字大小降序排列,最占空间的前 20 个文件一眼可见。定位到大文件之后,再用 ls -lh 看看它还在被哪个进程写入(lsof 可以确认):
bash复制lsof +L1 /var/log/bigfile.log
加 +L1 可以显示那些已被删除但还被进程持有的日志文件。很多时候即便你把日志文件 rm 掉,进程仍然持有旧文件句柄,磁盘空间不会释放。处理这种“删了但空间没回来”的问题,最有效的办法是:
bash复制# 既不删除文件,又能瞬间清空内容,句柄还不断
> /var/log/bigfile.log
用 > 重定向清空文件内容而不是 rm,这样进程持有的文件句柄依然有效,新日志照常写入,磁盘空间立刻释放。这是我在现场处理过无数次的经验,新手往往卡在“文件都删了为什么 df 还是 100%”这个反直觉的问题上。
另外,journald 日志也可能占满磁盘。journald 不像 syslog 老老实实受 logrotate 管理,它的容量上限由 /etc/systemd/journald.conf 里的 SystemMaxUse 控制,默认可能占系统盘很大一部分空间。检查一下:
bash复制journalctl --disk-usage
如果结果惊人,可以直接调整配置限制容量:
code复制[Journal]
SystemMaxUse=500M
改完重启 journald:systemctl restart systemd-journald。
4.3 syslog 日志服务器与日志集中管理
热搜里有“syslog日志服务器”和服务器集群相关词,说明很多人已经到了多台服务器需要统一管理日志的阶段。单机查看是基础,但到了几十台机器的时候,每台机器单独登录 tail 肯定是不行的,这时候就需要把日志集中收过来。
最简单的方案是基于 rsyslog 的集中日志服务器。原理非常简单:rsyslog 支持通过网络接收其他机器发来的 syslog 消息。集中服务器上开启 UDP 514 或 TCP 514 端口,各客户端通过 rsyslog 配置把日志转发过来。
比如在集中日志服务器的 /etc/rsyslog.conf 里启用:
code复制module(load="imudp")
input(type="imudp" port="514")
module(load="imtcp")
input(type="imtcp" port="514")
客户端服务器在 /etc/rsyslog.d/50-forward.conf 里写:
code复制*.* @192.168.1.100:514
这里 @ 代表 UDP,@@ 代表 TCP。UDP 速度快但可能丢包,TCP 更可靠。我建议内网环境用 TCP,日志消息保真更重要。
收过来之后怎么写?rsyslog 可以按来源主机名拆分存储:
code复制$template RemoteLogs,"/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log"
*.* ?RemoteLogs
这个配置会让每台客户端的日志都按主机名和服务名自动分类,查找时只要看对应目录,非常清爽。
但这套方案有一个非常现实的问题:rsyslog 只适合做 syslog 协议范围内的日志汇聚,对多行堆栈、JSON 结构日志、超大日志文件支持有限。到了微服务架构、容器化部署阶段,更多人会选择 ELK(Elasticsearch + Logstash + Filebeat + Kibana)或 Grafana Loki 这类体系。Filebeat 负责采集,Logstash 负责解析加工,Elasticsearch 负责存储和检索,Kibana 负责可视化展示。这套组合的强大之处在于全文检索、聚合分析和图形化报表,日志排障从“SSH 上服务器去 grep”变成了“在浏览器里几秒钟完成聚合查询”。
不过选型上我始终提醒一句:别盲目上重型方案。两三台机器就往 ELK 上堆,会有大量运维成本,反而增加复杂度。日志量小于每天几个 GB 时,用 rsyslog 集中 + grep 已经足够;到了一定的量级、需要多人协作检索、需要长期保存并随时聚合统计分析时,再上 Loki 或 ELK 更划算。Loki 相对 ELK 更轻量,因为它只在标签上建索引,对全文不做索引,查询时会扫描 chunk 再进行过滤,存储成本和运维成本低不少,但查询速度不如 ELK。有没有最优解?没有,只有最适合你现状的方案。
5. 常见问题与排查技巧实录
5.1 日志不写入 / 不更新,怎么排查
日志文件不更新是最诡异的故障之一。服务在跑,进程没死,但日志文件的时间戳就是不跳动,排查几个方向:
先确认是不是应用把日志写到别的地方了。很多 Java 应用配置了 logback 或 log4j2 的文件路径,不一定往 /var/log 写。先 lsof -p 进程PID | grep log 看看进程实际打开的日志文件路径是什么。如果 lsof 显示文件路径不存在了,说明之前被 logrotate 或手动 rm 过,但句柄没释放。
再检查是不是写日志文件本身被限制大小了:
bash复制# 查看进程的 ulimit 限制
cat /proc/<PID>/limits | grep -i file
如果打开文件句柄数撞到上限,服务依然在跑,但新日志写不进去,静默失败的情况很常见。
还有一个低频但真实的原因,是系统的时间和日志时间不一致。一个服务器时区错了,日志打出来的时间和真实事件对不上,看起来就像是“日志不更新”。排查办法很简单:
bash复制date -R
看时区是不是 UTC,如果是,而你期望是 UTC+8,那日志时间全部慢了 8 小时。很多跨境服务器默认 UTC,排查时特别容易把自己绕晕。建议在生产环境统一用 UTC 存储时间,界面展示时再转换为本地时区,这是一个避免大量误判的纪律。
5.2 日志刷屏、重复输出怎么处理
服务日志疯狂刷屏,一是占满磁盘,二是干扰真正的排障。处理停不下来的刷屏,关键在于分清是应用自身日志级别配太低(比如 debug 打全量信息),还是程序进入了错误循环。
如果应用用的是 logback/log4j2,找到配置文件把对应类的日志级别从 DEBUG 调到 INFO 或 WARN,通常能立即缓解。如果是因为某个方法跑飞了在死循环里打日志,需要先定位代码位置,再用 grep -c 统计刷屏内容的重复频率,辅助确认循环周期:
bash复制# 统计 10 秒内某行日志出现多少次
timeout 10 tail -f /var/log/app.log | grep -c "DeadLoopException"
timeout 10 让 tail 最多跑 10 秒,结束后输出这 10 秒内匹配的行数,基本能算出刷屏速率。
有一个很实际的技巧,是给日志配置按大小自动轮转,让刷屏不至于瞬时打爆磁盘。比如 logrotate 里把 rotate 7 改成 rotate 2,并把 size 参数设成 size 100M,一旦日志文件超过 100M 就轮转,即使应用在刷屏,也能把单文件控制在可接受范围。
5.3 日志中出现乱码与时间戳异常
日志乱码大多与字符集有关。一些老应用按 GBK 输出,而系统默认 UTF-8,终端里看到的就是乱码。查看时可以先确认文件编码:
bash复制file /var/log/legacy_app.log
如果是 gbk 等非 UTF-8 编码,可以临时转码查看:
bash复制iconv -f gbk -t utf-8 /var/log/legacy_app.log | tail -n 100
但更彻底的方案是统一应用字符集,生产环境一律 UTF-8。记住一个原则:日志文件编码应该是项目共享的约定,不能每个机器随便配。
时间戳异常通常是时区问题。日志里时间比当前时间早 8 小时、晚 8 小时都是常见的“坑”。排查时先确认应用运行时的 JVM 时区或系统时区,再回忆一下是谁改了 /etc/localtime。如果服务端统一用 UTC 存储日志,我建议所有人在排障时心里自动换算,而不是去“修”日志时间。
5.4 几个常见排查场景速查表
我把多年运维中遇到的高频日志排查场景整理成一个速查表,放在这里方便你直接对照使用。
| 现象 | 首选排查日志 | 常用命令 | 关键排查点 |
|---|---|---|---|
| 服务器登录慢/失败 | /var/log/secure 或 /var/log/auth.log | grep "Failed" /var/log/secure |
是否有大量 Failed password,是否有非预期 IP |
| 服务启动失败 | journalctl | journalctl -u 服务名 -n 200 --no-pager |
是否有 Permission denied、Address already in use |
| 磁盘空间突降 | /var/log 全目录 | du -ah /var/log | sort -rh | head -20 |
是否存在超大日志文件、被删除但占用空间的句柄 |
| 应用响应缓慢 | 应用日志 + Nginx access log | tail -f 观察请求耗时字段 |
是否有接口响应时间异常升高,是否伴随上游超时 |
| 定时任务不执行 | /var/log/cron | grep 任务名 /var/log/cron |
任务是否被调度,脚本是否有报错输出 |
| 网络/DNS 异常 | /var/log/messages 或 syslog | grep -i "dns|network" |
是否有网络断开、DNS 解析失败等内核级报错 |
| 内核/硬件问题 | dmesg | dmesg -T | tail -n 50 |
是否有 I/O error、thermal throttling、驱动加载失败 |
这张表不能替代你自己动手查日志,但它起码给了你一个起点。日志排查最大的成本不在命令,而在“第一步往哪个方向看”。方向对了,后面都是体力活。
6. 一些值得长期坚持的日志习惯
最后分享几个我踩过多次坑之后养成的习惯,谈不上高深,但每一个都救过我的命。
第一个习惯,是接收一个新环境时,先做一次日志摸底。把所有关键服务的日志路径、轮转策略、保存周期、占用空间记下来,整理成一页纸文档。别小看这一步,生产环境故障时大家都很紧张,有一份清晰的日志清单可以大幅缩短定位时间。目录结构不同、服务名不同、日志格式不同,这些都是新环境最容易绊倒人的地方。
第二个习惯,是善用别名和脚本把高频操作固化成快捷键。比如我在 .bashrc 里配了这几个别名:
bash复制alias nginxlog='tail -f /var/log/nginx/access.log'
alias nginxerr='tail -f /var/log/nginx/error.log'
alias syslog='tail -f /var/log/syslog'
alias applog='journalctl -u myapp --no-pager -n 100'
别小看这几个别名,故障发生时人处于高度紧张状态,记忆会短路,稳定的肌肉记忆比临场回忆重要得多。
第三个习惯,是设置日志文件大小告警。日志属于“温水煮青蛙”型问题,不会瞬间爆发,但日积月累一定会出事。我一般会在监控系统里设两个阈值:日志目录总占用超过 10GB 预警、超过 20GB 告警,然后定期检查 logrotate 是否真的在正常工作。与其等磁盘满再救援,不如提前让日志量暴露问题。
第四个习惯,是保持日志格式规范化。新规划一个应用的日志体系时,我会强制要求:时间戳用 ISO8601 格式、包含时区;每行日志包含服务名、模块名、日志级别、trace_id;异常堆栈单独缩进但保持连续关联。这些规范虽然不能直接解决故障,但在事后分析阶段能节省大量时间,尤其在链路追踪场景下,trace_id 简直是救命稻草。
说到底,日志查看不是一门高深的技术,它更像是一套纪律。命令就那些,文件路径也就那些,真正的差距在于遇到问题时,你能不能第一时间想到看什么日志、用什么姿势看、看到之后怎么判断。希望这篇指南能帮你在下一次故障发生时,少走一些弯路。
