微服务分布式日志环境完整搭建实战|从服务改造到 ELK 集群部署、全流程可落地

本文完整落地一套可用于测试环境的分布式日志整套环境,包含:SpringBoot 微服务代码、Docker 部署 ELK+Filebeat、配置文件、排错要点,全部配置复制即可运行,适配 CSDN 直接发布。

架构总览 SpringBoot 微服务输出日志文件(携带 MDC‑traceId) → Filebeat 采集本地日志 → Logstash 日志过滤解析 → Elasticsearch 存储索引 → Kibana 可视化检索查询

环境说明

  • JDK:17
  • SpringBoot:2.7.x / 3.x
  • 容器:Docker & Docker‑Compose(快速部署整套 ELK 栈)
  • ELK 版本统一:8.11.0,版本不一致极易出现兼容性报错

一、微服务端完整改造(输出规范日志文件)

1、pom 核心依赖

spring‑boot‑starter 内置 slf4j+logback,无需额外引入。

复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
</dependencies>

2、TraceId 工具类 TraceUtil.java

复制代码
import org.slf4j.MDC;
import java.util.UUID;

public class TraceUtil {
    public static final String TRACE_ID = "traceId";

    public static void setTraceId(String traceId) {
        MDC.put(TRACE_ID, traceId);
    }

    public static void setTraceId() {
        String traceId = UUID.randomUUID().toString().replace("-", "");
        MDC.put(TRACE_ID, traceId);
    }

    public static String getTraceId() {
        return MDC.get(TRACE_ID);
    }

    public static void clear() {
        MDC.remove(TRACE_ID);
    }
}

3、Web 过滤器,处理 HTTP 请求 TraceId

复制代码
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
public class TraceFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpReq = (HttpServletRequest) request;
        String traceId = httpReq.getHeader(TraceUtil.TRACE_ID);
        if (traceId != null && !traceId.isBlank()) {
            TraceUtil.setTraceId(traceId);
        } else {
            TraceUtil.setTraceId();
        }
        try {
            chain.doFilter(request, response);
        } finally {
            TraceUtil.clear();
        }
    }
}

4、Feign 拦截器,向下游传递 traceId 请求头

复制代码
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;

@Component
public class FeignTraceInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        String traceId = TraceUtil.getTraceId();
        if(traceId != null){
            template.header(TraceUtil.TRACE_ID, traceId);
        }
    }
}

5、logback‑spring.xml 完整配置(控制台 + 滚动日志文件输出)

重点:日志格式必须带上%X{traceId},输出到文件,给 Filebeat 采集。 文件输出路径:/opt/app/logs/app.log,后面 Filebeat 读取这个路径。

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <!-- 日志格式,务必保留traceId字段 -->
    <property name="LOG_PATTERN" value="%d{yyyy‑MM‑dd HH:mm:ss.SSS} [%thread] %‑5level traceId=%X{traceId} %logger{60} ‑‑ %msg%n"/>

    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF‑8</charset>
        </encoder>
    </appender>

    <!-- 文件滚动输出,Filebeat读取此文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/opt/app/logs/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/opt/app/logs/app‑%d{yyyy‑MM‑dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF‑8</charset>
        </encoder>
    </appender>

    <root level="INFO">
        <appender‑ref ref="CONSOLE"/>
        <appender‑ref ref="FILE"/>
    </root>
</configuration>

application.yml 简单配置

复制代码
logging:
  config: classpath:logback‑spring.xml

部署注意:服务器上要预先创建目录

复制代码
mkdir -p /opt/app/logs
chmod 755 /opt/app/logs

二、Docker Compose 一键部署 ELK 环境

新建docker‑compose‑elk.yml

注意:ES8 默认开启安全认证,本配置关闭安全,适合内网测试环境;生产务必开启账号密码。

复制代码
version: '3.8'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    container_name: es
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms1g -Xmx1g"
    ports:
      - "9200:9200"
      - "9300:9300"
    volumes:
      - es_data:/usr/share/elasticsearch/data
    networks:
      - elk‑net

  logstash:
    image: docker.elastic.co/logstash/logstash:8.11.0
    container_name: logstash
    ports:
      - "5044:5044"
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
    depends_on:
      - elasticsearch
    networks:
      - elk‑net

  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch
    networks:
      - elk‑net

volumes:
  es_data:
networks:
  elk‑net:

2.1 编写 Logstash 解析配置

在同级目录创建文件夹 logstash/pipeline,新建文件 log.conf

grok 正则用来解析我们 logback 输出的日志格式,提取 time、thread、level、traceId、logger、msg 字段。grok 模式必须和 logback 输出格式严格匹配,否则字段提取失败!

复制代码
input {
  beats {
    port => 5044
  }
}

filter {
  grok {
    match => { "message" => '%{DATA:log_time} \[%{DATA:thread}\] %{DATA:level} traceId=%{DATA:traceId} %{DATA:logger} ‑‑ %{GREEDYDATA:msg}' }
  }
  # 如果traceId为空,填充默认值,避免ES字段缺失
  mutate {
    default => { "traceId" => "unknown" }
  }
}

output {
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    index => "microservice‑log‑%{+YYYY.MM.dd}"
  }
  stdout { codec => rubydebug }
}

2.2 启动 ELK 集群

复制代码
# 启动
docker-compose -f docker-compose-elk.yml up -d

# 查看日志,观察是否报错
docker logs -f logstash

访问:

三、部署 Filebeat 采集微服务日志

Filebeat 不要放 docker 内部,直接安装在宿主机;需要读取宿主机/opt/app/logs/*.log日志文件。

filebeat.yml 完整配置

复制代码
filebeat.inputs:
- type: filestream
  enabled: true
  paths:
    - /opt/app/logs/*.log
  parsers:
    - ndjson:
        overwrite_keys: true
        add_error_key: true

output.logstash:
  hosts: ["127.0.0.1:5044"]

# 关闭elasticsearch直接输出,全部交给logstash处理
output.elasticsearch.enabled: false

logging.level: info

安装 filebeat(Linux)

复制代码
#下载安装包
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat‑8.11.0‑linux‑x86_64.tar.gz
tar -zxvf filebeat‑8.11.0‑linux‑x86_64.tar.gz
cd filebeat‑8.11.0‑linux‑x86_64
#替换自带filebeat.yml为上面配置
./filebeat -e -c filebeat.yml

四、Kibana 配置使用,按 traceId 查询全链路日志

  1. 访问 kibana http://ip:5601
  2. Stack Management → Index Patterns → 创建索引模式:microservice‑log‑*
  3. Discover 页面,就可以看到采集过来的所有日志。

检索示例:

  • 根据 traceId 查询整条调用链:traceId:"xxxxxxxxxxxx"
  • 查询 ERROR 级别异常:level:"ERROR"

五、整套环境常见坑与排错清单

  1. Filebeat 采集不到日志
    • 检查文件路径、文件权限,filebeat 进程要有读日志文件权限;
    • filebeat 控制台看输出,确认是否读到文件。
  2. Logstash grok 解析失败,traceId 字段为空 grok 正则和 logback 打印格式字符、空格、符号必须完全匹配,一个符号错就解析失败; logstash 控制台 stdout 输出可以看到原始 message 和解析后的字段,用于调试 grok。
  3. Feign 调用后 traceId 丢失
    • Feign 拦截器是否实例化成功;
    • 下游服务 TraceFilter 是否生效,请求头traceId是否透传。
  4. ES 磁盘爆满
    • 设置索引生命周期策略,7‑14 天自动删除旧日志;测试环境不要长期保留日志。
  5. 生产环境注意事项
    • ES 必须开启账号密码,不能裸奔;
    • Logstash 增加队列配置,防止消息积压丢失;
    • 高并发场景,Filebeat 输出到 kafka 做缓冲,再消费给 logstash,避免日志高峰压垮 ELK;
    • 禁止业务服务直接输出日志到 logstash,必须 filebeat 做采集层解耦。

六、生产架构升级思路

测试环境:微服务 → Filebeat → Logstash → ES → Kibana 生产高可靠架构:微服务 → Filebeat → Kafka消息队列 → Logstash消费Kafka → ES → Kibana 增加 Kafka 削峰,防止突发大量日志打崩日志系统。

总结

搭建分布式日志环境,不只是把 ELK 部署起来。 关键点分为三块:

  1. 业务代码层:MDC 生成、透传 TraceId,统一日志输出格式;
  2. 采集层:Filebeat 轻量采集,避免侵入业务服务;
  3. 存储检索层:Logstash 解析字段存入 ES,Kibana 按 traceId 检索全链路日志。
相关推荐
OsDepK13 小时前
项目快速Git至仓库(完整版)
大数据·git·elasticsearch·搜索引擎
Elastic 中国社区官方博客19 小时前
如何通过一条 ES|QL 查询为 Elasticsearch 中的每个指标构建指标图表
大数据·运维·数据库·elasticsearch·搜索引擎·全文检索·kibana
程序员梅雨21 小时前
微服务学习最终篇: Elasticsearch
学习·elasticsearch·微服务
Elasticsearch21 小时前
教程:使用 ES|QL 进行威胁狩猎
elasticsearch
智搜广告1 天前
GEO优化公司怎么选?智搜广告从三个维度帮你判断
大数据·人工智能·python·elasticsearch·microsoft·geo
Elastic 中国社区官方博客1 天前
Elasticsearch:ES|QL 搜索教程
大数据·数据库·人工智能·sql·elasticsearch·搜索引擎·全文检索
Elasticsearch1 天前
Elasticsearch 中的查询重写规则:通配符扫描速度提升 2.3 倍
elasticsearch
Elasticsearch1 天前
隐藏在可观测性数据中的安全攻击
elasticsearch
Elasticsearch2 天前
Elasticsearch:ES|QL 搜索教程
elasticsearch