十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

告别cat!GB级生产日志高效排查命令实战指南

告别cat!GB级生产日志高效排查命令实战指南 后端同学应该都遇到过这种场面线上接口告警刷屏你打开跳板机手忙脚乱地 cd 到日志目录顺手就是一个 cat app.log想着先看看报错长什么样结果屏幕瞬间被几十万行日志淹没终端卡到连 CtrlC 都要半秒才反应。更尴尬的是你翻了半天啥也没定位到反而把想找的报错信息冲得无影无踪。这篇文章不是来教你怎么用 cat 的而是要告诉你一件事在 GB 级生产日志面前cat 是最糟糕的选择。我会把后端性能排查里真正能扛事的实战命令全部摆出来从 tail、less 这些基础工具到 grep、awk、sed 的组合打法再到压缩日志、滚动日志的应对方案最后用一个完整的接口超时排查案例把整套思路串起来。无论你是刚接手线上运维的新手还是写了几年 Java、Go、Python 后端的老兵这套东西都能直接抄作业。先明确一个概念生产日志动辄几个 GB几十 GB 也不罕见这可不是你本地开发环境那个几十 MB 的小文件。cat 的基本逻辑是把整个文件内容全部输出到终端这个机制在超大文件下会带来一连串问题我们一个个说。1. 为什么说无脑cat是生产日志排查的坑1.1 cat 到底错在哪三个致命问题第一内存和 IO 被白白浪费掉。cat 会把整个文件从磁盘读进内存缓冲再一股脑写到标准输出。GB 级文件意味着你要读几个 GB 的数据这中间磁盘 IO、内存带宽、CPU 全都跟着遭殃。你只是想知道最后几条报错结果却让机器把整个文件嚼一遍典型的杀鸡用牛刀。第二输出量太大终端成重灾区。终端本身是不擅长显示大文本的你把几万行日志一次性喷到屏幕上轻则卡顿、滚动条拖不动重则你的 SSH 会话直接假死。而且日志里经常有各种控制字符、乱码、超长行终端渲染这些内容时更吃力这就是为什么很多人 cat 完大文件后,整个窗口像中了邪一样。第三也是最关键的cat 没有检索能力。它只是倒数据定位全靠肉眼在滚滚文字里捞针。GB 级日志里想找一个 10 分钟前发生的异常靠 cat 翻页基本等于大海捞针。你需要的是过滤抽取定位的能力cat 一个都给不了。1.2 动手之前三分钟看清日志文件底细排查任何日志问题前我建议你先花三分钟把现场摸清楚而不是闷头 cat。常用的是这几个命令# 文件大小看是不是真的大到不能碰 ls -lh app.log # 文件类型确认是纯文本还是压缩包 file app.log # 总行数知道敌人有多少兵力 wc -l app.log # 目录下所有日志文件按改动时间倒序知道哪些是最近在写的 ls -lht /usr/local/app/logs/ # 看看最后 20 行判断日志格式、时间戳样式 tail -n 20 app.log这一步特别重要。先看清文件大小、行数、日志格式、时间格式后面的命令怎么组合就有了依据。比如我看到时间戳是2025-01-15 10:23:45.123这种格式后面用 sed 按时间范围截取时就能直接写前缀匹配。要是日志格式是2025/01/15 10:23:45那又是另一种写法。动手前摸清底细比一上来就试命令靠谱得多。2. 基础三件套tail、head、less 的正确打开方式2.1 tail追日志才是核心刚需排查线上问题时绝大多数情况你想看的是最近发生了什么。这时候 tail 是绝对的主角。# 只看最后 100 行 tail -n 100 app.log # 实时跟踪文件新增内容最常用 tail -f app.log # 哪怕日志文件被轮转、改名也能跟着新文件继续输出 tail -F app.log # 配合 grep 做实时过滤只盯着 ERROR 和 Exception tail -f app.log | grep --line-buffered -E ERROR|Exception我重点说说tail -F这个大写参数。生产环境日志一般都有 logrotate 或类似机制在滚动文件可能被重命名成 app.log.1然后新建一个 app.log。如果你用的是小写-f文件句柄还指向旧文件你会发现日志不走了其实信息都在 app.log.1 里。-F会监听文件名的变化自动切到新文件上。这个坑我踩过不止一次线上追了半天日志发现追的是已经滚走的旧文件血压直接拉满。grep --line-buffered这个参数也要解释一下。默认 grep 在管道里是块缓冲的输出不一定实时加上--line-buffered后每匹配到一行就会立即输出配合 tail -f 做实时日志监控时体验完全不一样。2.2 less替代 cat 的分页查看神器如果你真的需要从头到尾看一个日志文件或者想在一个大文件里自由浏览、搜索less 是 cat 的完美替代品。它的设计哲学就不是全部输出而是按需渲染文件多大都能秒开。# 打开文件按 q 退出 less app.log # 打开时直接跳到某个时间点/关键词长日志特别好用 less app.log # 在 less 内搜索/ERROR 回车然后 n 下一个 N 上一个 /ERROR # 打开文件直接定位到第一次出现 ERROR 的位置 less -p ERROR app.log # 大写 F 跟随模式相当于 tail -f按 CtrlC 退出 less F app.log # 显示行号 less -N app.log # 超长行不换行用左右方向键平移查看 less -S app.logless 里我最常用的几个快捷键先列出来按键作用G跳到文件末尾gg跳到文件开头/word向下搜索关键词?word向上搜索关键词n/N下一个匹配 / 上一个匹配F进入跟随模式等价于 tail -fq退出这里要说一个经验排查问题的时候先less -p ERROR直接跳到第一个报错位置再顺着上下文往前推比从文件开头一页页翻效率高得多。less 打开超大文件的速度基本是即时的因为它只读取当前屏幕需要的那一部分数据这正是 cat 做不到的。2.3 head只在需要看开头时出场head 的出场率确实低一些但你排查服务启动失败配置加载异常这类问题时日志开头往往就是关键。比如 Spring Boot 应用启动失败错误堆栈可能就在前几十行。# 看前 50 行 head -n 50 app.log # 看文件中某个时间段之前的内容常配合管道 head -n 500 app.log | tail -n 50head 也可以用来做探路。面对一个几 GB 的未知文件先用 head 看前几行摸清格式再决定下一步用什么命令组合比直接无脑 cat 稳妥太多。3. grep、awk、sed 三板斧把 GB 级日志筛成几行3.1 grep先定位再查看排查看日志的核心思路是先过滤后查看而不是先查看再翻找。grep 就是那个过滤器。# 搜关键词显示行号 grep -n NullPointerException app.log # 搜出包含关键字的文件有哪些 grep -l NullPointerException *.log # 统计出现次数 grep -c ERROR app.log # 正则表达式搜索-E 是扩展正则 grep -E ERROR|FATAL app.log # 显示匹配行的前后 5 行上下文这个特别关键 grep -n -A 5 -B 5 NullPointerException app.log # 只输出匹配到的部分常用于提取某个字段 grep -o orderId:[0-9]* app.log # 反向过滤排除心跳/健康检查等噪音日志 grep -v heartbeat app.log这里我要强调-A和-B这两个参数。异常堆栈信息往往在 ERROR 之后的好几行光匹配 ERROR 看不到堆栈细节等于白干。-A 20直接把后续 20 行上下文带出来基本能覆盖 Java 异常栈的完整长度。-A后面跟多少行我一般先试 10不够再加别一上来就 100 行输出还是会太大。还有个小技巧grep 搜超长文本文件时可以用LC_ALLC临时关闭 locale 相关的字符处理提速非常明显。比如LC_ALLC grep -n timeout app.log实测在某些老机器上能快好几倍。3.2 awk按列提取、按条件统计grep 帮你定位到了行但日志里往往一行包含多个字段时间、线程号、日志级别、类名、消息内容。想按某一列做筛选、排序、统计就得请 awk 出场。# 按空格分隔打印第 1 列比如时间和第 7 列比如响应码 awk {print $1, $7} app.log # 自定义分隔符比如按逗号分隔提取 awk -F, {print $2, $4} app.log # 条件过滤第 12 列数值大于 500 的行 awk -F, $12 500 {print} app.log # 统计所有 ERROR 出现的次数 awk /ERROR/{count} END{print count} app.log # 按状态码分组计数 awk {code[$7]} END{for (k in code) print k, code[k]} app.log # 统计所有接口调用的耗时并按耗时排序 awk -F[,:] {print $2} app.log | sort | uniq -c | sort -rnawk 最典型的场景是统计。比如日志格式是时间,接口名,状态码,耗时ms你想知道哪个接口调用量最大、哪个接口平均耗时最高awk 一行就能出统计。生产环境做快速归因的时候这个能力比打开 Excel 分析快太多了。我经常用 awk 把耗时字段揪出来再管道给 sort 和 head直接看 Top 10 慢请求。3.3 sed按行区间和时间范围截取sed 在日志排查里最实用的功能是按行号或按正则范围切片。# 打印第 1000 到 2000 行 sed -n 1000,2000p app.log # 按时间范围截取从 10:00:00 开始到 10:30:00 结束 sed -n /2025-01-15 10:00:00/,/2025-01-15 10:30:00/p app.log # 既要在时间段内又要包含 ERROR可以用管道 sed -n /2025-01-15 10:00:00/,/2025-01-15 10:30:00/p app.log | grep -E ERROR|Exception # 删除空行看紧凑输出 sed /^$/d app.log按时间范围截取是我认为 sed 在日志领域最不可替代的场景。比如你知道线上 10:00 到 10:05 之间出了事用 sed 把这段时间的日志全部抠出来生成一个小文件后面随便怎么翻都方便。这个思路基本贯穿整个排查流程先按时间切片缩小到一个可控范围再在这个范围里做精细分析。3.4 管道组合一条命令搞定 80% 的排查单独的工具是零件管道组合才是完整的排查武器。分享几个我反复在用的组合拳# 实时追踪并过滤出 ERROR 和 Exception带行号 tail -f app.log | grep --line-buffered -n -E ERROR|Exception # 查看最近 1 万行里所有 ERROR并统计数量 tail -n 10000 app.log | grep -E ERROR | wc -l # 从最后 5 万行里找出耗时超过 1000ms 的慢请求按耗时降序取前 20 tail -n 50000 app.log | awk -F, {if ($4 1000) print $2, $4} | sort -k2 -rn | head -n 20 # 用 grep 先筛出目标请求 ID 相关的行再用 less 分页看上下文 grep -n order_id123456789 app.log | less # 把某段时间的日志截取出来再按关键字过滤 sed -n /2025-01-15 10:00:00/,/2025-01-15 10:30:00/p app.log | grep -A 20 NullPointerException error_snapshot.txt管道组合的原则是层层缩小范围。从 GB 级文件先砍到 MB 级再砍到 KB 级最后把命中的几行输出出来。整个过程都是流式处理内存占用极小这也是为什么这些命令能扛住超大文件的原因。我见过很多刚入行的同事一上来就 cat 整个文件然后用鼠标找就是没理解流式过滤这个概念。4. 压缩日志与滚动日志的处理4.1 zcat、zgrep、zless直接查压缩包生产环境为了省磁盘历史日志经常是 gzip 压缩过的文件名长这样app.log-20250115.gz。你要做的不是先解压再查而是直接用压缩感知命令。# 查看压缩包末尾内容用 zcat 配合 tail zcat app.log-20250115.gz | tail -n 50 # 在压缩包里搜关键字 zgrep -n NullPointerException app.log-20250115.gz # 分页查看压缩文件 zless app.log-20250115.gz # 如果文件是 bz2 格式用 bzgrepxz 格式用 xzgrep # 比如 bzgrep -n ERROR app.log-20250115.bz2zcat 和 zgrep 的原理是解压后直接管道输出不会在磁盘上产生临时文件省时省空间。这里提醒一句zcat 默认期望文件扩展名是 .gz如果遇到非标准后缀可以手动指定格式比如gzip -dc file.tar.gz2 | grep xxx之类的操作不过生产环境一般用不上。4.2 日志轮转之后怎么快速定位日志轮转后的场景很典型app.log、app.log.1、app.log.2.gz一长串文件你想找昨天下午 3 点的日志总不能一个个文件翻。我的做法是# 先按时间倒序列出所有日志找出目标时间段落在哪个文件里 ls -lht app.log* # 在所有 .log 和 .gz 文件里一起搜 zgrep -n ERROR app.log* | less # 只搜某一天的压缩日志 zgrep -n 2025-01-14 15: app.log-20250114*.gz | grep NullPointerException日志轮转文件的命名规则各不相同我见过按日期、按序号、按时间戳的排查前先ls -lht摸清文件清单是必要动作。这一步省不了因为跨文件搜错方向会浪费大量时间。4.3 多文件场景下的全局排查如果日志被拆成多个文件比如按业务模块拆、按实例拆你还得会跨文件搜索# 递归搜索目录下所有日志文件 grep -rn order_id123456789 /usr/local/app/logs/ # 在压缩包和普通文件里一起搜索 zgrep -n order_id123456789 /usr/local/app/logs/*.gz # 只列出哪些文件命中了关键字不输出具体行 grep -rl NullPointerException /usr/local/app/logs/多文件场景下-l列出文件名是个宝藏参数。先确认问题到底出在哪个应用/哪个实例的日志里再集中精力看那个文件比在整个目录里盲目搜高效得多。特别是服务通过负载均衡分发请求时同一个 requestId 可能出现在多个实例的日志里用grep -l先圈定实例范围能少走很多弯路。5. 实战演练一个接口超时问题的完整排查链路理论说了一大堆不如来个完整的实战。我拿前阵子排查过的一个 Java 后端服务举例症状是用户反馈下单接口偶发超时报错率不算高但就是不断。5.1 第一步先摸清当天的日志分布登录跳板机先看日志目录cd /usr/local/app/logs/ ls -lht看到当天生产日志已经滚成了order-service.log和order-service.log-20250115.gz。先用 wc -l 估算一下量级再把最近 50 行拿出来确认日志格式。tail -n 50 order-service.log日志格式类似2025-01-15 10:23:45.123 [http-nio-8080-exec-12] INFO OrderController - request start orderId123456789 userId987654 2025-01-15 10:23:45.512 [http-nio-8080-exec-12] INFO OrderService - call stock service cost387ms 2025-01-15 10:23:46.002 [http-nio-8080-exec-12] ERROR OrderController - order create timeout orderId123456789关键信息都在同一行里这给后面的 awk 统计打下了好基础。5.2 第二步先按时间窗口缩小范围用户反馈的集中时段是上午 10:00 到 10:30我直接用 sed 截出这个时间窗口保存成临时文件后面的操作都在小文件上做响应速度飞快。sed -n /2025-01-15 10:00:00/,/2025-01-15 10:30:00/p order-service.log /tmp/snapshot.log wc -l /tmp/snapshot.log如果目标时间段跨了文件比如正好在日志滚动的边界就把压缩包也一起处理zcat order-service.log-20250115.gz | sed -n /10:00:00/,/10:30:00/p /tmp/snapshot.log这是整个排查流程里性价比最高的一步把 GB 级文件变成几万行的小文件后面所有命令都能跑得飞起。5.3 第三步从 ERROR 到具体请求在这个时间窗口里搜异常grep -n -E ERROR|Exception /tmp/snapshot.log | head -n 50发现大量order create timeout报错并且每一条报错行都带着 orderId。接下来要确认这些超时请求是不是集中在某个下游服务于是用 awk 提取耗时字段并统计awk -Fcost {print $2} /tmp/snapshot.log | awk -Fms {print $1} | sort -rn | head -n 20这里直接看到了耗时最长的 20 次调用最长的已经到了 5 秒以上。再按报错接口聚合一下确认问题是不是集中在某个接口awk -FOrderService /ERROR/{print $2} /tmp/snapshot.log | sort | uniq -c | sort -rn统计结果把问题指向了下游库存服务的调用耗时飙升。5.4 第四步把异常上下文抠出来复盘完整链路定位到具体的 orderId 之后我把它在日志里出现的所有行全部抓出来看一条请求的完整生命周期grep -n orderId123456789 /tmp/snapshot.log结果发现这个请求在 10:23:45 发起调用库存服务耗时 387ms 本来很正常但紧接着下游返回异常重试逻辑又触发了一次调用最终整体耗时超了 5 秒。整个链路通过一个 orderId 串了起来问题原因从日志角度已经清楚了下游服务有偶发性慢调用加上重试策略放大了耗时。这个复盘过程如果还用 cat就是噩梦。你想想在一个几千万行的生产日志里靠 cat 去翻一个 orderId 的所有痕迹基本不可能。但用 grep 加 orderId 定位几毫秒就出来了。6. 常见问题与排查技巧实录6.1 高频翻车场景速查表我把实际排查中经常遇到的翻车场景和对应解决方案整理成一个速查表方便你直接对号入座场景错误做法正确姿势文件几个 GB只想看最新日志cat app.logtail -n 100 app.log日志文件被 rotatetail 显示不更新小写tail -f大写tail -F自动跟随新文件想看某个时间段的日志自己猜行号翻sed -n /起始时间/,/结束时间/p搜 ERROR 但看不到堆栈详情只用grep ERRORgrep -A 20 ERROR带出异常栈压缩日志没法搜先解压再搜zgrep、zcat、zless正则太慢直接搜LC_ALLC grep提速或先 sed 缩小范围想按字段统计肉眼数awk按分隔符提取列再统计多个日志文件找目标逐个文件 catgrep -l先定位文件再精读这个表格里的每一个正确姿势我都踩过对应坑。尤其 tail -F 和 sed 按时间截取这两个点可以说是生产日志排查中价值最高的两个命令。6.2 我私藏的几个小技巧技巧一给常用命令起别名。排查链路稳定之后我把常用组合写进了 bashrc工作效率提升非常明显。alias errtail -f app.log | grep --line-buffered -E ERROR|Exception alias snipsed -n /10:00:00/,/10:30:00/p alias slowtail -n 10000 app.log | awk -F, {if (\$4 1000) print} | sort -k2 -rn技巧二快速定位单行 JSON 日志。现在很多后端服务会输出 JSON 格式日志一行非常长直接看容易瞎。用 less 打开后加上-S参数长行会被截断显示再用左右键平移比默认自动换行清晰很多。技巧三排查顺序永远是时间窗口 → 关键字 → 字段统计不要一上来就抓瞎搜。我见到太多人拿到几 GB 日志直接 grep 一个词结果输出几十万行依然没法看。正确顺序是先缩小时间范围再缩小关键字范围最后用 awk 做统计输出一步步逼近问题本源。技巧四记得看日志文件编码。有些老系统日志是 GBK 编码grep 中文关键词时会搜不到。先用file app.log确认编码如果不匹配可以加iconv -f GBK -t UTF-8 app.log | grep xxx转换一下再搜。这个坑在老旧银行、电信系统里特别常见。技巧五如果文件实在太大比如超过 20GB建议先用 split 切分再并行处理。split -b 500m app.log part_能切成 500MB 的小块配合xargs -P 4并行 grep可以在几分钟内完成全量扫描。不过这只是极端场景的兜底方案绝大多数情况靠前面的管道组合就够了。最后再说几句体己话。我工作这几年见过太多人在生产日志面前手足无措第一反应永远是 cat然后被日志洪流淹没最后只能一脸无奈地去问运维能不能给我导个小的日志文件。但其实排查日志的本质不是看而是算和筛。你看再多的日志不如花几分钟想清楚我要找的数据在哪个文件里、在哪个时间段、长什么样、需要提取哪个字段。想清楚这些用什么命令心里就有数了。前阵子有个新同事看完我的排查过程感叹了一句原来不用 cat 也能查日志。我觉得这句话挺准确的你不是不能用 cat而是没必要用。GB 级日志面前tail、less、grep、awk、sed、zcat 这套组合拳才是真正吃饭的家伙。把这套东西练熟下次线上告警来了你打开终端的那一瞬间心里是踏实的。
返回列表