第3章:日志分析实战:日志规范、ELK/EFK栈搭建与日志分析技巧
3.1 日志规范:没有规矩,不成方圆
做SRE这些年,我见过最头疼的事是什么?
不是系统挂了,而是系统挂了之后,日志里全是「Error: something went wrong」。
你想想看,这跟没写有什么区别?
日志规范,说白了就是给系统装个「黑匣子」。我个人的习惯是,所有日志必须包含三个要素:时间戳、级别、上下文。
核心原则:
- 时间戳精确到毫秒:别用默认格式,用 ISO 8601。我遇到过因为时区没对齐,排查了三个小时的坑。
- 日志级别要统一:ERROR 就是真的错误,WARN 是潜在风险,INFO 是正常流程。别把 INFO 当 DEBUG 用。
- 带上请求ID或TraceID:微服务架构下,没有这个你根本连不起来调用链。
举个例子,一个合格的日志长这样:
2025-03-15T14:23:45.123Z [ERROR] [order-service] [trace_id=abc123]
用户ID=98765 下单失败:库存不足 (商品ID=1001, 请求量=5, 库存量=2)
嗯,这里要注意:不要打印敏感信息。密码、身份证号、信用卡号,一律脱敏。我曾经见过一个同事,直接把用户明文密码打到了日志里,结果被安全团队约谈了一下午。
3.2 ELK/EFK栈搭建:选对工具,事半功倍
日志有了,怎么收集?怎么查?
ELK(Elasticsearch, Logstash, Kibana)和 EFK(Elasticsearch, Fluentd, Kibana)是目前最主流的方案。我个人更倾向于 EFK,因为 Fluentd 比 Logstash 更轻量,内存占用少一半。
3.2.1 架构选型对比
| 组件 | ELK | EFK |
|---|---|---|
| 采集器 | Logstash(Java,重) | Fluentd(Ruby/C,轻) |
| 存储/检索 | Elasticsearch | Elasticsearch |
| 可视化 | Kibana | Kibana |
| 适用场景 | 复杂数据转换 | 高吞吐、低资源消耗 |
我建议:中小团队直接上 EFK。Logstash 的 filter 虽然强大,但配置复杂,而且吃内存。Fluentd 的插件生态也很丰富,够用了。
3.2.2 快速搭建 EFK(Docker Compose 版)
别怕,我直接给你一个能跑起来的配置。这是我项目里一直在用的模板:
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- "9200:9200"
fluentd:
image: fluent/fluentd:v1.16
volumes:
- ./fluentd.conf:/fluentd/etc/fluentd.conf
- /var/log:/var/log
ports:
- "24224:24224"
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
Fluentd 的配置文件 fluentd.conf 长这样:
<source>
@type tail
path /var/log/app/*.log
pos_file /var/log/fluentd.pos
tag app.log
format json
</source>
<match app.log>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
flush_interval 5s
</match>
小技巧: 记得加上 pos_file。这样 Fluentd 重启后不会重复读取旧日志。我曾经因为没加这个,导致 ES 里数据翻了一倍,排查了半天才发现。
3.3 日志分析技巧:从海量数据里捞针
日志堆起来了,怎么快速定位问题?
我总结了三个实战技巧,你直接拿去用。
3.3.1 巧用 Kibana 的 Lucene 语法
别只会搜关键词。Lucene 语法能帮你精准过滤:
level: ERROR AND service: order-service—— 只看订单服务的错误message: "timeout" AND response_time: >5000—— 超时超过5秒的请求NOT level: INFO—— 排除所有 INFO 日志,只看异常
我个人习惯:先按时间范围缩小,再用关键词过滤。别一上来就搜全量,ES 会哭的。
3.3.2 聚合分析:从个体到全局
单个错误可能只是偶发,但聚合后就能看到趋势。
在 Kibana 里,用「Visualize」功能做柱状图:
- X轴:时间(按分钟聚合)
- Y轴:错误数量
- 拆分:按 service 字段
这样一眼就能看出:哪个服务在哪个时间段出了问题。
避坑指南: 我曾经遇到过一个 case,某个服务每整点报错一次,但每次只报一条。单看日志觉得「就一条,没事」,但聚合后发现:这个错误每天出现24次,持续了三个月。最后发现是定时任务没处理好异常。所以,一定要做聚合分析。
3.3.3 日志关联:TraceID 是救命稻草
微服务架构下,一个请求会经过多个服务。怎么串起来?
答案就是 TraceID。每个服务在入口处生成一个 TraceID,然后透传到所有下游服务。日志里都带上这个 ID。
在 Kibana 里搜 trace_id: abc123,就能看到这个请求的完整生命周期:
- API Gateway 收到请求(INFO)
- Order Service 处理订单(INFO)
- Payment Service 扣款(ERROR:余额不足)
- Order Service 回滚(WARN)
你看,问题出在 Payment Service,而不是 Order Service。如果没有 TraceID,你可能还在 Order Service 的日志里瞎找。
3.4 实战案例:一次线上故障的日志排查
最后,我分享一个真实案例。
去年某天晚上,用户反馈「下单一直转圈」。我登录 Kibana,按时间倒序查 ERROR 日志:
2025-03-15T20:00:01.000Z [ERROR] [payment-service]
DB connection pool exhausted (active=50, max=50)
嗯,数据库连接池满了。但为什么?
我继续搜 payment-service 的日志,发现大量 query timeout 的 WARN 日志。再搜数据库慢查询日志,发现有一条 SQL 执行了 30 秒。
最终定位:某个新上线的报表查询,没加索引,把数据库打满了。
整个过程,从接到报警到定位根因,用了不到 10 分钟。这就是日志规范 + 好工具 + 分析技巧的力量。
总结一句话: 日志不是写给机器看的,是写给未来的自己看的。规范一点,排查时能少掉几根头发。