尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux日志查看与运维排查:tail、grep、journalctl

Linux日志查看与运维排查:tail、grep、journalctl 1. 先把日志这件事想明白再谈命令凌晨两点被告警叫醒SSH 登上机器第一件事不是重启服务而是翻日志。这个场景做过运维、做过后端、甚至做过测试的人都熟。Linux 查看日志的命令看着就那么几个但真正到了现场能不能在五分钟内定位到问题靠的不是背命令而是知道日志在哪儿、该用哪把刀、以及这把刀在什么情况下会钝掉。我把日常接触到的日志查看需求归成四类实时跟踪服务正在报错盯着看下一秒发生什么、回溯定位已经出事了找某个时间点的上下文、统计分析不是看单条而是看出现频次、分布规律、归档留存出了问题要能翻出三个月前的记录。这四类需求的解法完全不同混着用就会事倍功半。这份内容适合谁看刚接手服务器、只会cat一下/var/log/messages的新同学写过几年业务、但排查问题时总是靠搜索引擎现查参数的熟手以及需要给团队定日志规范、纠结留存周期和轮转策略的技术负责人。我会把每个命令的坑、每个参数背后的原因、以及我在真实故障现场踩过的雷都摊开讲。提醒下面涉及的/var/log/路径在不同发行版上差异不小。RedHat 系偏向/var/log/messages、/var/log/secureDebian 系偏向/var/log/syslog、/var/log/auth.log新版 systemd 体系则大量内容进了 journal。拿到一台陌生机器先用ls /var/log/摸清地形再动手。1.1 Linux 日志的几种来源分类从生产者角度看日志大概分五路。第一路是内核它把启动信息、硬件探测、OOM 击杀、磁盘 IO 错误写到内核环形缓冲区用dmesg读。第二路是 systemd 体系几乎所有由 systemd 托管的服务stdout 和 stderr 默认都被 journald 收走用journalctl读。第三路是传统的 syslog 守护进程按设施和优先级把消息分流到不同文件比如认证相关的进 auth.log邮件相关的进 mail.log。第四路是应用程序自己管理的日志文件路径和格式由开发者决定可能是单文件也可能按天分目录。第五路是容器运行时收集的日志Docker 默认把容器的标准输出落到宿主机的 json 文件里。从写入方式看又分直接写文件和交给别人写。直接写文件的应用比如 Nginx自己控制格式和切割排查时直接看文件最直接。交给 syslog 或 journald 的应用日志会被加上自己的元数据PID、单元名、优先级这时候硬去翻原始文件反而找不到。从生命周期看日志要么滚动切割后被压缩归档要么在内存里转一圈就没了。最怕的是那种既不切割也不清理的应用日志——我见过一个服务把日志写成单文件 40 多 GB磁盘报警了才发现用less打开要等半分钟用tail倒是秒开但想按时间查历史基本没戏。1.2 工具选型的判断顺序拿到问题先别急着敲命令按这个顺序问自己三句话。第一句日志在哪儿是普通文件、journal、还是容器运行时里路径确认错了后面的命令全是白费。有个笨办法很好用systemctl status 服务名 -l会直接告诉你这个服务的日志在哪里、最近几条是什么。第二句我要的是流还是点如果服务正在持续报错需要跟着看那就是流式需求tail -F、less F、journalctl -f三选一。如果是查某个已经发生的事件那是点式需求重点在过滤条件怎么写。第三句数据量多大几 MB 的文件随便看cat都行几百 MB 用less配合搜索上了 GB 就必须先过滤再读否则光是 IO 等待就能耗掉你十分钟。下面这张表是我平时给团队新人看的快速对照先建立印象后面再逐个拆开。需求场景首选命令理由盯住正在写入的日志tail -F文件被切割后仍能跟住边跟边翻历史less F可暂停、可搜索、可恢复从大文件里捞关键字grep -n -C带行号和上下文看系统服务与启动日志journalctl -u -b自带时间、优先级、单元维度看内核与硬件异常dmesg -T直接读内核环形缓冲区看容器输出docker logs --tail不用进容器内部统计错误分布grep awk sort uniq把文本当数据算查历史归档zgrep/zless直接读压缩包无需解压2. 四个基础命令覆盖八成日常需求基础命令之所以基础是因为它们足够简单、足够可靠在任何一台机器上都不用额外安装。但简单不等于没讲究这四个命令各自的边界在哪里、参数怎么选值得单独说清楚。2.1 cat、tac 和 head 的适用边界cat是最容易被滥用的命令。它的本职工作是拼接和输出用在日志上只有一种场合合适文件很小几十行到几百行你想整篇通读。几个实用参数cat -n 文件加行号方便跟同事描述第几行有问题cat -A 文件把制表符、行尾符、不可见字符都显示出来排查这文件看着没问题但程序解析报错这类诡异现象时特别管用。cat的反向是tac从最后一行往前输出。查日志时它的价值在于你最关心的往往是最新的记录用tac 文件 | head -100就能拿到最后 100 行并且保持新的在前的阅读顺序。不过说实话这个场景用tail -100更直接tac真正好用的地方是处理那种按行追加、但你想倒着遍历的清单文件。head的正经用途是看开头。日志文件的开头往往记录了启动参数、版本号、配置加载结果出问题时这些信息很关键但平常没人会主动去看。养成一个习惯接手新服务时先head -50看一眼它的启动日志你对这个服务的初始化过程就有底了。注意绝对不要在几十 GB 的日志上直接cat。终端会疯狂刷屏CtrlC都不一定马上停得住更糟的是如果这台机器同时跑着关键业务大量输出经过终端渲染会吃掉不少 CPU。判断文件大小用ls -lh或du -h超过 10 MB 就别cat了。2.2 tail -f 和 -F这是最容易踩的坑tail是查看日志用得最多的命令没有之一。基础用法tail -n 200 app.log看最后 200 行tail -n 1 app.log从头看全部号表示从第几行开始。真正需要重点讲的是-f和-F的区别。-f是--follow它跟踪的是文件描述符准确说是 inode。-F是--followname --retry它跟踪的是文件名。这两者差在哪儿生产环境的日志几乎都会轮转。logrotate 到了时间点通常的做法是把app.log重命名成app.log.1然后让程序重新创建一个新的app.log。这时候如果你用的是tail -f app.log你还盯着老文件的 inode 看新写入的内容全在新建的app.log里你的终端会一片死寂——很多人以为服务挂了其实只是跟丢了。而tail -F app.log发现文件被重命名后会重新打开继续跟住新的app.log。所以结论很简单在生产环境看持续写入的日志一律用-F。-F还有一个好处是--retry文件暂时不存在时不退出等到它被创建出来就自动跟上适合服务正在重启的窗口期。再补充两个实战参数。tail -f -n 0表示不显示历史内容只看从现在开始新写入的日志这在日志量特别大、你只关心接下来会发生什么时很有用。tail -F 文件 | grep --line-buffered 关键字是常用的组合注意--line-buffered不能省否则 grep 会因为输出缓冲把匹配结果攒着你要等很久才看到一行失去了实时性。2.3 less 才是读大日志的正确姿势如果说tail是望远镜less就是显微镜加地图。它的核心优势是按需加载——打开一个 5 GB 的文件几乎是瞬间的因为它只读你正在看的那一屏。less最被低估的用法是F。less F app.log打开后表现得跟tail -f一样会持续滚动显示新内容。但关键在于此时按CtrlC它会停下来然后你就可以像正常less一样往上翻、往下翻、搜索、跳转。翻够了再按F或者ShiftF它又恢复跟踪模式。这个随时停下来回看历史再随时恢复跟随的能力是tail -f完全做不到的。我的习惯是排查线上问题时永远用less F而不是tail -f因为报错往往是一闪而过的需要立刻往回翻几屏确认上下文。less里必会的几个操作G跳到最后一行g跳到第一行/关键字向下搜索?关键字向上搜索n和N在匹配项之间跳转-N在打开时显示行号也可以在less里直接敲-N回车切换。关键字是个隐藏技能它只显示包含关键字的行把大文件过滤成小视图等价于临时做了一次 grep 但不丢原始位置。还有一个参数处理端特别重要——less -R。很多日志带 ANSI 颜色转义码直接打开是一堆ESC[32m之类的乱码。-R会让它正确渲染颜色。如果你还遇到过中文显示成\xe4\xbd\xa0这种十六进制那通常是文件编码问题跟less无关需要用file命令确认编码后用iconv转换。2.4 把输出当管道组合起来才叫本事单个命令的能力有限日志分析真正的威力在管道。举几个我日常在用的组合。tail -F app.log | grep --line-buffered -E ERROR|Exception | awk {print strftime(%H:%M:%S), $0}这条给每条错误日志前面加上当前时间因为很多应用的日志本身不带时间戳或者时间戳格式不统一加一层本地时间便于观察错误发生的节奏。tail -n 10000 app.log | grep 用户ID12345 | head -50先限制范围再过滤避免在大文件上做全量扫描。head放在最后能在拿到结果后立刻让管道提前结束节省资源。less F app.log按CtrlC停住敲ERROR只留错误行再敲F恢复跟随。这套动作熟练之后定位速度比任何图形化工具都快。3. grep 家族在海量日志里精准捞针grep是日志分析的绝对核心但大部分人对它的使用停留在grep 关键字 文件。真正拉开效率差距的是上下文参数、正则表达式以及和其他文本工具的配合。3.1 -A、-B、-C 上下文参数怎么用单看一行报错信息量往往不够。异常堆栈是多行的业务上下文可能在前几行后续的收尾信息在后几行。这三个参数解决的就是这个问题-A nAfter显示匹配行及其后面 n 行-B nBefore显示匹配行及其前面 n 行-C nContext前后各显示 n 行实测下来排查应用报错用grep -n -C 5 ERROR app.log最舒服。5 行上下文通常足够看清哪个请求触发的、报了什么错、程序怎么处理的。如果是 Java 的异常堆栈-A 30可能都不够因为堆栈可能很长这时候更高效的做法是用sed -n /Exception/,/^$/p这种范围提取把从异常开始到下一个空行的整段抓出来。-n显示行号这个参数建议默认加上。有了行号你就能用sed -n 1234,1300p app.log精确定位那一段或者用less 1234 app.log直接跳到那个位置继续看。3.2 正则、多条件与性能取舍grep -E开启扩展正则可以写grep -E ERROR|WARN|FATAL这类多分支匹配。grep -i忽略大小写适合那种一会儿 error 一会儿 Error 一会儿 ERROR 的日志。grep -v反向匹配用来排除噪音——比如日志里全是健康检查的记录你只想看业务请求grep -v healthcheck就能清场。grep -c只输出匹配行数快速判断某个错误是不是在刷屏这个在评估影响面时特别有用。性能上有个很多人不知道的优化纯字符串匹配用grep -F。-F表示把模式当固定字符串而不是正则表达式省去正则引擎的编译开销。在处理几 GB 的日志时grep -F比默认正则模式快好几倍。另外-r递归搜索整个目录时要格外小心如果目录里有巨型日志文件一次grep -r可能几分钟都跑不完还会产生大量磁盘 IO 影响同机其他服务。稳妥的做法是先grep -rl只列出匹配的文件名再对具体文件做细查。还有两个容易被忽略的参数-a把二进制文件当文本处理。有时候日志里混进了不可打印字符grep 会直接告诉你 Binary file matches 然后不显示内容加-a就能正常输出。-o只输出匹配到的那部分配合正则可以实现从每行里把 IP 地址抠出来的效果。3.3 和 sort、uniq、awk 配合做统计分析单条日志看内容海量日志看分布。把日志当成结构化数据来算往往能得到单看日志发现不了的规律。最经典的组合是统计错误类型排行grep ERROR app.log | awk -FERROR {print $2} | sort | uniq -c | sort -rn | head -20先捞出所有 ERROR 行用 awk 切掉前面的时间戳部分剩下错误内容排序去重计数再按次数倒序前 20 名就是最需要优先修的问题。这个套路我在做老系统治理时用它做过一次全量统计结果发现排第一的是一个早就被忽略的空指针每天刷几十万条清掉之后日志量直接降了一大截。按时间维度聚合也很实用。如果日志格式是2024-05-20 14:23:45 ...可以这样统计每小时的出现次数grep ERROR app.log | awk {print $1 $2} | cut -c1-13 | sort | uniq -ccut -c1-13截出 2024-05-20 14 这个小时粒度聚合后就能看出错误集中在哪个时间段。如果是每天定时任务触发的问题这种图景一眼就能识别出来。3.4 按时间窗口精确截取日志分析里最常见的诉求之一是只看今天上午 9 点到 10 点之间发生了什么。有两种做法。如果日志行首是标准格式的时间戳用 awk 做字符串比较最快因为时间格式相同的情况下字典序比较和实际时间顺序是一致的awk $1 $2 2024-05-20 09:00:00 $1 $2 2024-05-20 10:00:00 app.log如果时间戳格式不统一或者你只想按两个标记之间的内容截取用 sed 的范围模式更灵活sed -n /09:00:00/,/10:00:00/p app.log这个方法的限制是它会匹配所有出现的起止标记如果同一时间点在日志里重复出现比如多个并发请求结果会包含多段内容需要你自己判断。实操心得分析一个已知的时间段时先用grep -n定位到起止行号再用sed -n 起行,止行p精确截取比直接按内容匹配更可控。定位行号这一步看起来多余但它把模糊匹配变成精确切片不会误伤。4. 系统层面的日志journalctl、dmesg 与登录记录应用日志看业务逻辑系统日志看机器本身是不是出问题了。这两者经常要交叉看因为一个应用崩溃根因可能是内存被内核 OOM 杀掉也可能是磁盘满了导致写不进去。4.1 journalctl 的过滤姿势journalctl是 systemd 时代最重要的日志工具因为它按结构化字段存储日志可以按单元、时间、优先级、启动次数等维度过滤比翻文本文件精确得多。必会的几个参数-u 服务名只显示某个 systemd 单元的输出这是排查服务启动失败的第一选择通常配合-b一起用表示只看本次启动以来的记录。-b -1看上一次启动的记录这个在排查服务是不是重启过时特别有用。--since 2024-05-20 09:00 --until 2024-05-20 10:00按时间窗口过滤时间字符串支持today、yesterday、1 hour ago这类自然语言写法实测很好用。-p err只看错误级别及以上。journald 的优先级从 0emerg到 7debug用-p指定一个级别后只会显示该级别及更严重的。排查故障时journalctl -p err -b能立刻把本次启动以来的所有错误捞出来比翻一堆 INFO 快得多。-f是持续跟随相当于 tail 的行为。-k只看内核消息等价于dmesg。journalctl -o json-pretty这个用法值得单独提一下。它把每条日志以 JSON 格式输出包含_PID、_COMM、_SYSTEMD_UNIT、MESSAGE等完整字段。想用 awk 或 jq 做复杂分析时JSON 格式比默认的文本格式好处理得多。有个坑必须说默认情况下 journal 可能是非持久的。很多发行版把Storage设成auto表示如果/var/log/journal目录存在就持久化不存在就只放在内存里。内存里的日志重启就没了。如果你排查一个昨天半夜出的问题登录进去发现 journalctl 什么都查不到八成就是这个原因。解决办法是建目录并重启服务sudo mkdir -p /var/log/journal sudo systemd-tmpfiles --create --prefix /var/log/journal sudo systemctl restart systemd-journald持久化之后还要控制占用空间否则 journal 能长到几十 GB。编辑/etc/systemd/journald.conf设置SystemMaxUse500M或SystemMaxUse10%再配合SystemMaxFileSize控制单个文件大小。4.2 dmesg 看内核与硬件异常dmesg读的是内核环形缓冲区缓冲区大小固定写满之后新的会覆盖旧的所以它天然没有长期历史。想保留就得在启动早期用dmesg -w或者靠 journald 的-k帮忙存。常用参数dmesg -T把内核的相对时间戳转换成人类可读的日期时间不加这个参数你会看到[12345.678]这种相对秒数很难对应到实际时间。dmesg -w持续跟随输出。dmesg -l err,warn只看错误和警告级别。dmesg -H输出更易读带分页。排查什么用它内存不足触发的 OOM Killer 会在这里留下明显的记录能看到被杀的进程名和当时的内存状态。磁盘出现坏道或 IO 超时会有大量报错往往伴随文件系统被重新挂载为只读——这是个很严重的信号说明存储层面出问题了。网卡链路反复 up/down 也在这里可能指向网线、光模块或交换机端口的问题。4.3 关机、重启与登录记录容器化之前很多问题会表现为机器半夜自己重启了这时候要看的就是重启记录。last -x是最直接的工具它会显示 shutdown、reboot、runlevel 这几类特殊事件配合正常的登录记录一起列出。加-F显示完整的日期时间而不是简写的星期几。last -x reboot | head -10就能看到最近十次重启的时间和间隔。如果发现重启时间分布很有规律比如每天凌晨固定时间那大概率是某个定时任务或监控脚本触发的。who -b直接输出系统的最后一次启动时间比翻记录快。uptime配合看负载如果负载很高但机器刚起来不久说明有服务在启动阶段就在抢资源。登录相关的排查用last看成功登录lastb看失败登录这个需要 root 权限读/var/log/btmp。如果lastb里有大量来自同一 IP 的失败记录说明有人在尝试暴力破解这就涉及安全加固的话题了但至少你通过这条命令能及时发现。w看当前登录的用户和他正在执行的命令who更简洁一些只列登录会话。注意lastb输出可能非常长先lastb | head -50看最近的再决定要不要深入。另外如果系统启用了日志远程转发本机的这些记录可能只是副本真实完整的记录要去日志服务器上找。5. 容器与数据库日志场景化的查看方式现代部署环境下应用日志的形态发生了很大变化。容器里的应用通常不再自己写文件而是把内容输出到标准输出由运行时接管数据库则有自己一套专用的日志体系格式和查看方式跟普通文本日志完全不同。5.1 docker logs 的几个关键参数容器日志的第一入口是docker logs。基础用法docker logs 容器名但直接跑往往刷出几百屏所以一定要配参数。--tail 200只看最后 200 行-f持续跟随--since 10m只看最近 10 分钟支持10m、2h、2024-05-20T09:00:00这类格式-t在每行前面加时间戳。--until可以指定截止时间配合--since实现时间窗口查询。有个场景需要注意容器如果崩溃重启过docker logs默认只显示当前这次运行的日志崩溃前的那部分要用--previous简称-p才能看到。排查启动就崩的问题时这个参数是必备的。Docker 默认用json-file日志驱动日志实际存在宿主机/var/lib/docker/containers/容器ID/容器ID-json.log。这个文件如果不加限制会无限增长我见过把宿主机磁盘写满的例子最后整个节点上的所有容器都受影响。解决办法是在运行容器或配置 daemon 时限制大小{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 5 } }这段配置放在/etc/docker/daemon.json里效果是单个日志文件最大 50 MB最多保留 5 个总占用不超过 250 MB。对绝大多数服务来说够用了。另外两个小技巧docker logs的输出如果卡住不动可能是容器没有输出到标准输出而是写到了容器内的文件这时候要么docker exec进去看要么在构建镜像时就规范日志输出方式。用 docker compose 的场景下docker compose logs -f 服务名可以一次跟踪多个容器的输出并且按容器名加了前缀比逐个docker logs方便。5.2 数据库慢查询日志怎么定位业务日志告诉你这个接口慢数据库日志才能告诉你为什么慢。MySQL 的慢查询日志需要先打开在配置文件里设置slow_query_log 1、long_query_time 1单位秒1 秒通常是个合理起点、slow_query_log_file /var/log/mysql/slow.log。改完要重启或SET GLOBAL生效。打开之后超过阈值的 SQL 会连同执行时间、扫描行数、返回行数一起记录。直接看慢查询日志文件能看懂但信息密度低、不方便排序。更好的方式是用mysqldumpslow这个官方工具比如mysqldumpslow -s t -t 20 /var/log/mysql/slow.log按总耗时排序取前 20 条。如果要更细致的分析可以上 pt-query-digest 这类第三方工具它能生成聚合报告直接告诉你哪类 SQL 消耗了 80% 的数据库时间。Redis 的慢日志是另一套机制它不写文件而是放在内存里。用slowlog get 100取最近 100 条每条包含执行时间、耗时微秒数、命令本身。开关和阈值由slowlog-log-slower-than控制单位是微秒比如设成 10000 表示超过 10 毫秒就记录。这个参数设太小会拖慢 Redis 本身设太大又抓不到问题我的经验是把阈值定在业务可接受延迟的一半左右。5.3 binlog 能删吗以及为什么不能直接看先说结论binlog 不能随手rm。它跟普通日志的价值不一样——binlog 是 MySQL 的二进制日志主要用途是主从复制和数据恢复是数据资产而不是运行记录。你rm掉它轻则主从复制断掉重则某个时间点的数据恢复能力直接丢失。那磁盘满了怎么办正确做法是用 purge 命令按时间或按文件清理PURGE BINARY LOGS BEFORE 2024-05-01 00:00:00; PURGE BINARY LOGS TO mysql-bin.000123;更好的做法是设置自动过期。MySQL 8.0 用binlog_expire_logs_seconds老版本用expire_logs_days。设成 7 天或 14 天是比较常见的平衡点取决于你的备份策略和主从延迟容忍度。另一个高频疑问是为什么我less打开 binlog 全是乱码。因为它本身就是二进制格式不是设计给人直接读的。要查看内容必须用mysqlbinlog工具解析mysqlbinlog --base64-outputDECODE-ROWS -v /var/lib/mysql/mysql-bin.000123 | less加上-v会把行事件解码成可读的伪 SQL--base64-outputDECODE-ROWS让被编码的行数据也展开。这样你就能看到具体是哪张表、执行了什么操作。排查数据莫名被改了这类问题时binlog 是唯一能精确还原操作的证据源。5.4 crontab 任务日志去哪了定时任务不输出日志是新手最常遇到的困惑之一。任务确实执行了但看不到任何痕迹。首先要明白cron 本身的执行记录和任务本身的输出是两回事。执行记录由 cron 守护进程写RedHat 系在/var/log/cronDebian 系混在/var/log/syslog里。用grep CRON /var/log/cron或journalctl -u crond能看到某个时刻执行了某个命令。如果这里都没有记录说明 cron 服务本身有问题任务根本没被触发。任务自己的输出echo 的内容、脚本里的报错默认会尝试通过邮件发送而大多数机器没配邮件服务这些输出就直接丢了。所以写定时任务时一定要显式重定向0 3 * * * /opt/scripts/backup.sh /var/log/backup.log 2121把标准错误也重定向到同一个文件缺了它报错信息还是会丢。更规范的做法是在脚本内部自己加时间戳因为 log 里连续多天的输出会混在一起光看内容分不清是哪天跑的。加一行echo [$(date %F %T)] 任务开始就能解决。6. 日志不丢不乱轮转、留存与集中采集前面讲的都是怎么看这一节讲怎么管。日志管理的水平直接决定了出事时你能不能查到东西。6.1 logrotate 配置逐项拆解logrotate 是 Linux 上的标准日志轮转工具由 cron 或 systemd timer 每天触发。配置分两部分主配置/etc/logrotate.conf定义全局默认值各服务的配置放在/etc/logrotate.d/下。一份典型的配置长这样/var/log/myapp/*.log { daily rotate 30 compress delaycompress missingok notifempty create 0640 appuser appgroup sharedscripts postrotate systemctl reload myapp /dev/null 21 || true endscript }逐项说。daily表示每天轮转一次也可以是weekly、monthly或按大小触发。rotate 30保留 30 个历史文件超出就删最老的。compress用 gzip 压缩旧文件压缩率通常在 90% 以上日志这种高重复度的文本压缩效果极好。delaycompress表示最近一次轮转的文件先不压因为服务可能还持有它的文件句柄立刻压缩可能出问题推迟一轮更稳。missingok是文件不存在也不报错避免因为某个日志目录被清了导致 logrotate 报一堆噪音。notifempty空文件不轮转。create定义新文件创建时的权限和属主这个很重要。如果不写新文件会沿用旧文件的权限但如果旧文件权限被误改过问题会一直传下去。显式声明能保证每次轮转后权限都是对的。关于copytruncate和postrotate的选择这里有个真实的取舍。postrotate的方式是重命名旧文件、创建新文件然后通知程序重新打开日志句柄。这种方式最干净没有日志丢失但要求程序支持重新打开日志比如 Nginx 的reopen、多数守护进程的SIGHUP。对于那些不支持、或者不方便通知的程序只能用copytruncate先复制一份再把原文件清空。这个方式的问题是复制和清空之间有一个微小的时间窗口新写入的日志可能丢失。所以能用postrotate就别用copytruncate。配置改完一定要测试别等到出事那天才发现语法错了。用logrotate -d /etc/logrotate.d/myapp做 dry-run-d会打印详细过程但不实际执行。确认没问题后再用logrotate -f /etc/logrotate.d/myapp强制执行一次观察结果是否符合预期。6.2 留存周期和容量怎么估算被问得最多的一个问题是日志该留多久。这不是拍脑袋决定的得算。先估算日增量。假设一个中等规模的服务每天产生 100 万条日志每条平均 200 字节那么原始体积是 100 万 × 200 字节 ≈ 190 MB。日增量约 200 MB。然后考虑压缩。文本日志的 gzip 压缩率一般在 85% 到 95% 之间取保守值 85%压缩后每天约 30 MB。最后乘上留存天数。要保留 180 天总占用约 30 MB × 180 ≈ 5.4 GB。这个量级对现在的磁盘来说完全可控所以保留半年在技术上根本不是难题难的是有没有人去做这个配置。反过来说如果不压缩也不清理200 MB × 180 36 GB就有点肉疼了。有些团队的内部审计要求日志保留半年以上做法通常是双保险本机用 logrotate 保留 7 到 30 天应对日常排查同时用采集工具把日志转发到集中存储长期留存放在那边。这样本机磁盘压力小需要追溯时去日志平台查。6.3 集中采集方案的选择思路本机查日志有个天花板多台机器、多个容器的情况下你不可能挨个登上去 grep。这时候就需要集中采集。选型时有个关键决策点索引全量内容还是只索引标签。像 Elasticsearch 这类方案会把日志正文建索引查询能力极强能对任意字段做全文检索和聚合代价是存储成本高、写入吞吐有上限。而 Loki 这类方案只对标签比如服务名、主机名、环境建索引正文压缩后原样存储查询时先按标签缩小范围再暴力扫描内容。它的存储成本低很多写入吞吐也更高适合中小规模团队代价是查询不知道在哪个服务里的全量搜索会很慢。我的实际经验是日志总量在每天几十 GB 以下Loki 这类轻量方案足够超过这个量级、或者对查询复杂度要求很高再考虑重量级方案。无论选哪个采集前先在本机过滤是个通用原则。把 DEBUG 级别日志、健康检查记录这类噪音在源头就丢掉能省下一大半存储和带宽。7. 常见问题与排查速查表这一节把我在实际工作中反复遇到的问题整理出来大部分都是不知道原因时会觉得非常诡异知道原因后一秒解决的类型。7.1 几个高频诡异现象tail -f 突然不输出了。九成九是日志被轮转了文件被重命名-f还盯着老 inode。换成tail -F立刻恢复。这个坑我至少踩过三次后来直接把-F写进了肌肉记忆。grep 提示 Binary file matches 但不显示内容。日志里混进了不可打印字符grep 认为这是二进制文件。加-a强制当文本处理即可。日志里的中文全是乱码或者出现\r。多半是编码不一致最常见的是程序按 GBK 写终端按 UTF-8 读。用file 日志文件看编码声明用iconv -f GBK -t UTF-8 源文件 新文件转换。\r是 Windows 换行符的残留用dos2unix或sed -i s/\r$//处理。journalctl 查不到历史日志。检查/var/log/journal目录是否存在不存在就是内存存储模式重启即丢。按第 4.1 节的方法建目录并配置Storagepersistent。磁盘满了但du找不到大文件。有个文件被删除了但还有进程持有它的文件句柄空间没释放。用lsof L1找出这类文件然后重启持有它的进程。这个现象在日志文件被手动rm之后特别容易出现。容器日志时间戳和宿主机的对不上。容器里默认是 UTC 时区。启动时加-e TZAsia/Shanghai或者在镜像里设置好时区文件。差 8 小时的时间戳会让人在排查时完全失去方向感。7.2 常用操作速查表我想做的事命令实时跟踪日志且不怕轮转tail -F app.log边跟边翻历史less F app.log找错误并带上下文grep -n -C 5 ERROR app.log统计错误类型排行grep ERROR app.log | sort | uniq -c | sort -rn | head按时间段截取sed -n /09:00/,/10:00/p app.log查某个服务的系统日志journalctl -u 服务名 -b -f只看本次启动的错误journalctl -p err -b看内核异常dmesg -T -l err,warn查重启记录last -x reboot | head查失败登录lastb | head -50需 root看容器最近日志docker logs --tail 200 -f 容器名看容器崩溃前日志docker logs --previous 容器名看 MySQL 慢查询mysqldumpslow -s t -t 20 slow.log解析 binlogmysqlbinlog -v mysql-bin.000001 | less看定时任务执行记录grep CRON /var/log/cron直接读压缩日志zgrep 关键字 app.log.2.gz测试 logrotate 配置logrotate -d /etc/logrotate.d/myapp8. 我在实际排查中总结的几条经验最后这部分不按技术点组织就是一些纯经验的碎碎念都是踩过坑之后形成的条件反射。先确认时间再找关键字。这是我吃过最大的亏。有一次排查一个接口偶发超时我盯着日志里的 ERROR 关键字找了两小时最后发现真正的问题出在一个 WARN 级别的慢 SQL 记录上而那个 ERROR 是另一个无关的小问题。后来我形成了习惯先看告警时间点用时间窗口把日志范围框死再在这个范围里找异常效率高得多。用 grep -c 先判断量级。在深入分析之前先grep -c数一下匹配行数。是 5 条还是 50000 条决定了你接下来该用less慢慢看还是用sort | uniq -c做统计。不看量级直接输出很容易被淹死。别在业务高峰期跑大范围搜索。grep -r整个日志目录、解析几 GB 的归档文件这些操作会吃掉大量磁盘 IO。如果这台机器上跑着对 IO 敏感的服务你的排查动作本身就可能引发新的故障。稳妥做法是把文件复制到/tmp或另一台机器上分析或者用ionice -c3降低 IO 优先级。长时间跟踪用 tmux 或 screen。tail -F挂在 SSH 会话上网络一抖就断了历史输出全没了。放进 tmux 里跑断开重连后还能看到这段时间的记录。这个小习惯帮我保住过好几次关键的故障现场。关键输出用 tee 存一份。命令 | tee /tmp/debug.log既能看又能留。排查结束后把文件保存到问题单里下次遇到类似问题可以直接对照。动手前先保留现场。如果怀疑日志被轮转覆盖先cp一份再操作。用 文件清空日志之前一定要确认这个文件不重要——这个操作是不可逆的我见过有人清空日志时手抖多打了一个字符把配置文件给清了。把高频命令做成 alias 或小脚本。我在每台常用机器上都放了一份.bash_aliases里面有alias errloggrep -iE error|exception|fatal -C 3这类快捷方式。看起来只是省了几个字符但在紧张的时刻能少想一步就少一分出错的可能。日志这件事工具就那么多真正拉开差距的是排查思路和现场经验。把上面这些命令练熟再经过几次真实故障的洗礼你自然就形成了自己的判断顺序。
返回列表