第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,就能看到这个请求的完整生命周期:

  1. API Gateway 收到请求(INFO)
  2. Order Service 处理订单(INFO)
  3. Payment Service 扣款(ERROR:余额不足)
  4. 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 分钟。这就是日志规范 + 好工具 + 分析技巧的力量。

总结一句话: 日志不是写给机器看的,是写给未来的自己看的。规范一点,排查时能少掉几根头发。