资讯详情

资讯详情

服务日志分析实战:从采集链路到告警策略

早年间我带过一个小团队接到一个比较棘手的线上问题某核心服务每隔几天就会在凌晨两三点出现一次超时持续时间很短十分钟后自己恢复。因为没有监控告警等我们发现的时候日志早就被系统轮转覆盖掉了连点痕迹都没留下来。最后只能靠猜给服务加了一堆毫无根据的超时重试参数后果可想而知。那次之后我想明白一件事日志不是用来“出事之后查的”而是用来“出事之前读的”。你平时怎么对待日志决定了你在故障中的逃生概率。这个行业里干了将近二十年我越来越认同一句话日志分析能力才是服务端工程师和运维工程师真正拉开差距的地方。这一篇是系列的第一篇我打算先把服务类日志分析的整个方法论骨架搭起来——讲清楚日志的本质是什么、采集链路怎么选型、系统级服务与应用级服务各自的读法以及一整套能够直接落地的分析策略。后续的020篇、003篇再逐步深入具体工具和场景。这一篇适合刚接触Linux服务维护的人也适合那些已经写了不少脚本、却始终觉得“日志治理差点意思”的同行。1. 日志分析的本质先搞懂你要守护的三种“证据链”很多人觉得日志分析就是会看日志会 grep会 tail出了问题能翻到报错信息就行。这其实是把“看日志”和“做分析”混为一谈了。真正的日志分析首先要想清楚一个问题你手里的日志到底记录了什么、丢失了什么、为了什么而存在。从服务运维的角度日志可以拆成三条完全不同的“证据链”它们的用途、关注点、分析方式都截然不同。第一条是操作留痕链。这类日志告诉你“人或者程序对系统做了什么”典型代表是系统认证日志、sudo 记录、应用的关键操作日志。它的核心价值在审计和追溯一旦发生越权操作或误删数据你要能通过这条链还原出完整的操作时间线和执行者。分析这条链时关心的不是性能指标而是完整性和不可抵赖性。第二条是状态演化链。这类日志记录了服务运行过程中各项指标的变化比如MySQL的慢查询日志、Nginx的访问日志、各类中间件的运行日志。它的核心价值在于让你看到服务的“体感温度”——什么时候开始变热什么时候开始异常什么时候到达临界点。分析这条链时你是在做趋势判断而不是单纯找错误。第三条是故障现场链。这是应用程序自己打印的错误堆栈、异常退出信息、核心服务日志中的 ERROR / FATAL 级别记录。它是排障时最重要的原始证据但也是所有人最容易搞砸的——要么日志级别设置不合理关键信息没打出来要么日志量太大真正出错的那条被大量无用信息淹没。把这三条链想清楚再看具体日志文件你就不会眉毛胡子一把抓。拿 Linux 服务器来说/var/log/secure属于操作留痕链/var/log/messages或 journald 属于状态演化链应用自己目录下的 error.log 属于故障现场链。不同链条的数据要用不同的策略去采集、存储和分析。这一节最后我想强调一个很多人忽略的点日志是“序列化”的证据而分析和排障是“反序列化”的过程。日志产生时如果丢了信息后面无论用什么高级分析手段都补不回来。所以日志分析策略的源头不是“怎么分析”而是“怎么记录”。2. 服务日志的采集链路从零散文件到统一管线的选型逻辑日志这玩意儿最大的特点就是散。一个服务可能同时往好几个地方写日志系统日志进 journald业务日志进自己目录下的文件访问日志进另外的目录慢查询日志又跑到数据库数据目录里。等你需要汇总分析的时候单机处理已经捉襟见肘更别提几十台上百台的集群规模。所以“日志策略”的第一步永远是先把采集链路捋直。这一步的决策质量直接决定后面所有分析工作的效率和成本。2.1 先分清三种采集模式目前市面上主流的日志采集模式有三种我分别说下适用场景。直接读文件型代表工具是 Filebeat 这类轻量级采集器。它们模拟tail -F的逻辑盯着指定的日志文件新写入的行会被读走并发送到下游。优点是侵入性极低不要求应用改任何代码日志写到哪它就读到哪。缺点是对多行日志处理比较麻烦比如 Java 异常堆栈跨好几十行如果采集器不配置多行合并规则一条异常就会被拆成几十条碎片。远程协议接收型代表是 syslog 协议和各类 Agent 的远程上报。应用通过标准协议把日志主动发到远端不落地到本地磁盘。优点是集中度高、实时性好缺点是应用需要改造而且一旦网络抖动日志可能静默丢失。这类模式比较适合网络设备和安全设备的日志收集在应用服务上不建议作为唯一采集方式。日志目录挂载型主要用于容器和 Kubernetes 场景。通过 Docker 的 json-file 驱动或者 k8s 的日志路径映射把容器日志落到宿主机目录再由宿主机的采集器统一收集。这是云原生环境下的主流方案。需要注意的是容器重建后日志文件会重新创建采集器必须能正确处理“文件被轮转、重新创建、inode 变化”这一类情况。2.2 为什么我不建议直接在服务器上跑 grep有些同行习惯性争论“到底需不需要上日志采集系统我直接在服务器上 grep 不香吗”。对于一两台服务器、业务不复杂的场景直接上服务器 grep 完全合理成本最低见效最快。但是一旦规模上来直接 grep 的问题就暴露了。第一个问题是检索效率和并发压力。日志量动辄几十 GB每次 grep 都是全文件扫描内存和磁盘 IO 的压力相当大。线上服务高峰期你上去 grep 日志等于给本来就忙碌的磁盘引擎再踩一脚油门。第二个问题是无法做跨主机的关联分析。你在一台机器上 grep 到了一条报错但这条报错到底是网关层的问题还是数据库的问题你需要同时看三台机器、五个文件的时间片段靠单机 grep 做不到。第三个问题是无法做趋势和基线分析。grep 只能回答“有没有出现这个字符串”回答不了“这个错误在过去二十四小时内的频率变化趋势”。没有频率和趋势就谈不上策略和预警。所以我的建议是单机验证排障直接命令行没问题但是想要“策略化”管理日志趁早搭一条采集分析管线。2.3 日志管线选型要趁早定下的四个参数在选型阶段有四件事一定要定清楚不然后面返工成本极高。时区规范所有服务统一使用 UTC 或本地时区的某个固定时区绝不允许混用。日志时间戳不统一是跨系统分析的第一大坑。时间格式强烈建议统一采用 ISO 8601 格式带时区偏移量例如2024-06-15T08:30:0008:00。人类可读、机器可排序、跨语言解析无歧义。切割策略按天切割还是按小时切割取决于日志量和分析粒度。日志量大的服务建议按小时切割避免单个日志文件过大影响采集和分析效率。保留周期日志保留多少天、归档多少天、销毁哪些敏感字段要提前规定。一般来说热数据保留 7 到 15 天温数据归档 90 天超过 90 天的按合规要求处理。这四个参数定完之后采集链路就有了明确的“约定”后续无论上 Filebeat、Logstash、Loki 还是简单的 rsyslog 转发都只是工具层面的选择不会伤筋动骨。3. 系统级服务日志的拆解journald、cron、syslog 这些“地基”怎么看系统级日志是所有应用服务的地基也是最容易被忽略的部分。很多人的日志策略只盯着业务日志系统日志常年不看结果等到磁盘写满、定时任务失效、用户登录异常才想起来翻/var/log往往已经晚了。3.1 journald现代 Systemd 系统的日志中枢如果你的服务器跑的是 CentOS 7 以上、Ubuntu 16.04 以上那绝大部分系统服务的标准输出和错误输出都会被 systemd-journald 捕获通过journalctl命令查看。很多人只会journalctl -u 服务名这一条命令但实际用起来远远不够。我最常使用的几个组合是这样的。查看某个服务的最近日志并持续跟踪journalctl -u nginx.service -f查看某个时间段内的日志注意时间格式journalctl --since 2024-06-15 08:00:00 --until 2024-06-15 09:30:00 -u nginx.service按优先级过滤只显示 error 及以上级别journalctl -u nginx.service -p err这里要特别提醒一个关键点journald 默认把日志存在内存和少量磁盘中重启后可能丢失。如果你希望系统日志持久化保存必须创建/var/log/journal目录并设置好权限否则服务重启后你拿不到上一次运行时的崩溃信息。mkdir -p /var/log/journal systemd-tmpfiles --create --prefix /var/log/journal以及在/etc/systemd/journald.conf里建议设置Storagepersistent SystemMaxUse4G MaxRetentionSec1monthSystemMaxUse是日志空间上限MaxRetentionSec是最大保留时长。这两个参数不设的话journald 可能把磁盘塞满导致系统服务异常——这是非常典型的“日志反噬业务”的案例。3.2 cron 日志定时任务是最容易“哑火”的服务cron 定时任务的日志是系统日志里最容易被忽略的“盲区”。默认情况下 cron 的执行结果会写到/var/log/cron具体路径因发行版而异但这个文件记录得极其简略只有“执行了哪个任务、以什么用户身份执行”不会记录任务的执行输出。定时任务脚本的输出默认会通过邮件发送给 root 用户。很多服务器根本没配邮件服务导致这些输出直接丢失一旦脚本出错你看到的只有“任务执行了”看不到“任务执行失败了”。对定时任务我的建议很简单所有重要脚本务必自己输出日志到指定文件并在脚本尾部做退出码检查。比如#!/bin/bash exec /var/log/cron-backup.log 21 echo backup start: $(date %F %T) /usr/local/bin/backup_db.sh if [ $? -eq 0 ]; then echo backup success else echo backup FAILED fi每天定时巡检时看一眼这个日志文件的修改时间和末尾内容比任何花哨的监控方案都来得直接。3.3 syslog老协议不老rsyslog 仍然适用虽然 journald 已经很普及但 rsyslog 依然大量存在尤其是一些老系统、网络设备、以及需要把日志转发到集中式服务器的场景。很多管理员对 syslog 的认知停留在“日志存到 /var/log/messages”这就有点可惜了。rsyslog 的配置逻辑其实是“规则驱动”。你可以通过简单的规则把不同 facility 和 priority 的日志路由到不同文件甚至转发到远端。举个例子如果想把所有 auth 相关的日志独立出来# /etc/rsyslog.d/auth.conf auth.* /var/log/auth_secure.log更实用的是把系统日志转发到集中式日志服务器比如通过走 TCP 转发走 UDP 转发# 在 /etc/rsyslog.conf 末尾添加 *.* log-server.example.com:514转发配置写好后记得重启 rsyslog 服务并用logger命令测试链路是否打通logger -t test rsyslog forwarding test message然后在远程服务器上查看是否收到。别看这几步简单实际生产环境中“配置了转发但没测试、结果收不到日志”的情况我见过太多次了。3.4 系统日志巡检的“十分钟法”聊完具体组件我再分享一下我自己的系统日志巡检习惯。每天上班第一件事我固定花十分钟做三件事last -20查看登录记录确认没有异常登录检查/var/log/messages或journalctl -p err昨天的错误日志查看磁盘空间使用率尤其是/var/log所在的目录。这三件事能挡住很大一部分因为日志膨胀或者认证异常而产生的潜在故障。4. 应用级服务日志的实战盲区从 Nginx 访问日志到 MySQL 慢查询讲完了系统骨架真正花时间的重头戏是应用级日志。这类日志量大、种类杂、价值密度差异悬殊很多人要么一股脑全收要么只盯着错误日志看其实两种做法都有问题。4.1 Nginx 访问日志不止是看状态码要读出“体感”Nginx 访问日志是最典型的高价值日志。默认格式之下一条日志记录了客户端 IP、时间、请求方法、URI、状态码、响应字节数、Referer、User-Agent 等关键信息。但很多人只是看了“哪些 URL 返回 404、哪些返回 500”这属于最低级的分析。我更推崇的做法是自行设计 log_format把响应时间$request_time和上游响应时间$upstream_response_time加到日志格式里。这样访问日志才能真正反映出用户体感。推荐的 log_format 配置log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $request_time $upstream_response_time;加了$request_time之后你能做的事情就多了。用一行命令可以算出 P95 响应时间awk {print $(NF-1)} /var/log/nginx/access.log | sort -n | awk {a[NR]$1} END {print a[int(NR*0.95)]}这样算出的小于 100ms说明整体健康如果超过 500ms就要看是哪些接口拖慢的。然后可以按 URL 聚合找出 TOP10 慢接口awk {print $7, $(NF-1)} /var/log/nginx/access.log | sed s/\?.*// | awk {sum[$1]$2; cnt[$1]} END {for (k in sum) print k, sum[k]/cnt[k], cnt[k]} | sort -k2 -rn | head -20这里要提醒一个容易踩的坑访问日志格式一旦上线解析脚本就要跟着锁定。如果哪次加字段忘了同步解析脚本后面所有统计都会错位。所以修改 log_format 时要同步修改采集解析侧的配置或者干脆在解析端通过字段名映射而不是死板地按列数取值。4.2 MySQL 慢查询日志数据库性能分析的入口MySQL 慢查询日志是另一个“分析价值极高但默认没人开”的日志类型。慢查询日志默认是关闭的需要手动开启SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 2; SET GLOBAL slow_query_log_file /var/log/mysql/slow.log;long_query_time建议先从 2 秒开始评估观察几天后再决定是否调低。如果业务本身比较简单可以直接设成 1 秒甚至 0.5 秒抓全量慢查询。开了慢查询日志之后常用分析工具是mysqldumpslow用法很直接mysqldumpslow -s t -t 10 /var/log/mysql/slow.log按执行时间排序取出最慢的十条 SQL。这个工具还会自动把 SQL 里的数字参数归一化成抽象模板方便你看出“某类 SQL”慢而不是“某条特定 SQL”慢。不过慢查询日志有个特性经常被忽略它记录的是执行完之后的“总耗时”并不区分是 CPU 等待、锁等待还是 IO 等待。我见过不少同事看到一行慢 SQL 就直接去优化 SQL 结构结果优化了半天毫无效果。正确的姿势是结合EXPLAIN看执行计划并查看当时数据库的锁状态以及系统层面的 IO 压力。日志给你定位了“谁慢”但“为什么慢”需要多维度证据交叉验证。4.3 应用中间件日志别让 INFO 淹没 ERROR应用中间件日志比如 Redis、消息队列、注册中心这些组件的运行日志最能体现一家公司的日志水位。很多中间件默认日志等级是 INFO甚至 DEBUG导致一台服务器每天产生几个 GB 的日志。等真正出问题时要么磁盘早就写满要么要定位的错误信息被海量 INFO 日志淹没了。我的建议是中间件日志等级统一提升到 WARN业务应用保留 INFO但关键业务路径上必须明确打印关键节点的关键参数。日志级别不是面子工程是成本工程。每一条多余的日志都在消耗磁盘空间和 CPU 资源而这些成本最终都会以“机房账单”或“排障时间”的形式落到你头上。以 Redis 为例设置日志级别loglevel warning logfile /var/log/redis/redis-server.log改成 warning 级别之后日常日志量会下降一个数量级但只要 Redis 出现 key 过期淘汰、内存超阈值、持久化失败这类关键事件日志里依然有迹可循。4.4 应用日志格式化的统一规范聊到应用日志就绕不开日志格式。我见过太多应用日志是“想到什么打印什么”的风格导致一行日志里信息残缺不全分析端无从下手。统一的应用日志格式我推荐的字段组合是这样的时间戳 | 日志级别 | 服务名 | 主机名 | 线程名 | 请求追踪ID | 类名 | 业务消息对于 Web 服务一定要把请求追踪 IDTrace ID打进去。哪怕一时半会不上全链路追踪系统至少要做到“同一个请求的日志可以随意关联”。实现方式很简单在入口处生成 UUID放进日志上下文业务代码里直接打印即可。这样排障时你只要拿到一个 Trace ID就能把所有相关日志全部串起来效率提升非常明显。5. 日志分析策略的闭环数据保鲜期、告警分级与日常巡检前面聊完了日志的“读取”接下来进入真正的策略层面。这部分的逻辑骨架可以用一句话概括日志策略的目标不是在故障发生那一刻找到原因而是让故障根本没有机会伤害到业务。5.1 数据保鲜期日志不是留得越久越好很多人提到日志保留第一反应是“留得越久越安全”。但实际上日志留存是有成本的热数据的存储成本、检索响应速度、磁盘空间占用、合规风险都是实实在在的负担。我常用的数据保鲜期规范如下表所示供你参考具体可以根据业务调整日志类型在线查询保留归档保留销毁逻辑系统基础日志15 天90 天到期自动清理应用运行日志7 天30 天按需延长访问日志30 天180 天涉及用户 ID 的需要脱敏安全审计日志180 天1 年严格权限控制禁止随意删除这个表的价值在于它把“留多久”从拍脑袋变成了一条可执行的规则。尤其安全审计日志这类数据在合规审计时一旦缺失影响很大。我见过有些公司所有日志统一保留 3 天结果合规检查时拿不出半年前的操作记录只能被罚。所以日志策略一定要分层不要一刀切。5.2 告警分级日志从“事后查”变成“事前防”的关键一步日志分析最有价值的应用场景莫过于告警。把日志里的关键指标转成告警条件在故障伤害到用户之前触发人工介入这才是“策略”二字的灵魂。告警分级我习惯用三个等级P1 级服务不可用类。比如 Nginx 连续返回 5xx 超过 20 次/分钟或者进程崩溃退出。这类告警必须立刻通知值班人员夜深了也要爬起来处理。触发条件要严格宁可漏报不可误报。P2 级性能劣化类。比如 P95 响应时间连续 5 分钟超过 500ms或慢查询数量突增。这类告警不需要半夜响铃但上班后必须第一时间处理。P3 级噪音分析类。比如某个 ERROR 级别日志在 1 小时内出现超过 100 条。这类告警一般不直接通知人而是进入每日巡检简报由工程师浏览后判断是否升级处理。在设计告警阈值时有一个关键心法阈值不是拍脑袋拍出来的要看历史基线。你先在没有告警的情况下观察两周的日志指标取历史数据的 P99 作为“正常上限”在此基础上乘以系数作为告警触发值。没有基线的告警和猜大小没什么区别。5.3 每日巡检简报把日志分析变成日常习惯告警解决的是“突发”巡检解决的是“渐变”。很多故障是慢慢变坏的比如磁盘剩余空间从 70% 一路降到 20%中间没有任何一个时刻触发告警阈值但只要最后那一步稍微快了点就可能直接打爆磁盘。这就是巡检存在的价值。我自己有一套很轻量的巡检脚本每天定时执行把关键指标汇总到一个 Markdown 文件里发到邮箱。包含的内容大致是日志分区磁盘使用率以及变化趋势昨天出现的 ERROR 级别日志按服务聚合统计Nginx 5xx 状态码数量及对应 URL TOP 列表MySQL 慢查询数量以及 TOP 类目journald 占用空间及最近一次清理时间这套巡检不需要复杂的监控平台几行 shell 脚本加 crontab 就能跑起来。但做和不做在半年后的区别就非常明显了一个是“心里有数”一个是“等故障来找你”。5.4 日志策略的定期复盘最后我要强调一个容易被忽视的策略环节——定期复盘。我的习惯是每次线上故障处理完之后都要回答三个问题为什么日志里没有提前暴露这个隐患后续要在日志里增加什么指标才能提前发现同类问题当前日志格式和采集链路是否有阻碍排障的地方复盘的目的不是追责而是让日志策略本身不断进化。很多团队的日志策略一年不动服务架构早就变了多少次了日志链路却还停留在老样子。这种“策略僵化”才是日志治理失败的最大原因。6. 001篇收尾从读懂日志到设计日志中间缺的只是体系最后一节我不做什么总结就说点实在的个人体会。在这个行业摸爬滚打十几年我发现日志分析能力的成长路径其实可以分三个阶段第一阶段是“会看”能读懂常见服务日志会用 grep、awk 解决问题第二阶段是“会查”面对一个故障能快速定位日志位置、过滤关键字、结合时间线还原现场第三阶段是“会设计”从日志格式的定义、采集管线的搭建、告警阈值的制定、保留周期的规划一套体系信手拈来。大多数人一直停在第一、第二阶段不是不勤奋而是缺少一个从“被动看日志”到“主动设计日志”的思维转换。而完成这个转换的关键恰恰是把这个系列的文章里提到的每一个细节都拆开揉碎落地到自己的服务器上试一遍让它们成为你日常工作中的肌肉记忆。我这里再分享一个小技巧随便找一台非互联网业务的服务器关掉教科书式的思路尝试回答下面五个问题——这台机器上一周内有哪些非 root 用户登录过昨晚定时任务有没有全部成功执行昨天最慢的三个接口分别是哪些MySQL 有没有执行时间超过 2 秒的查询磁盘还要多久会被日志写满把这五个问题答完你大概就知道自己当前的日志体系处在什么水位了。001篇的内容就先到这里。整体还比较基础和框架化下一篇我打算具体聊聊“日志采集链路从零搭建的操作细节”把这几年在 Filebeat、Logstash、Kafka、Elasticsearch 链路里的实践经验拉出来晒一晒包括一些非常具体的配置参数和踩坑记录。感兴趣的朋友可以持续关注也欢迎在评论区分享你自己的日志治理心得。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →