本文完整落地一套可用于测试环境的分布式日志整套环境,包含: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
访问:
- ES: http://ip:9200
- Kibana: http://ip:5601
三、部署 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 查询全链路日志
- 访问 kibana http://ip:5601
- Stack Management → Index Patterns → 创建索引模式:
microservice‑log‑* - Discover 页面,就可以看到采集过来的所有日志。
检索示例:
- 根据 traceId 查询整条调用链:
traceId:"xxxxxxxxxxxx" - 查询 ERROR 级别异常:
level:"ERROR"
五、整套环境常见坑与排错清单
- Filebeat 采集不到日志
- 检查文件路径、文件权限,filebeat 进程要有读日志文件权限;
- filebeat 控制台看输出,确认是否读到文件。
- Logstash grok 解析失败,traceId 字段为空 grok 正则和 logback 打印格式字符、空格、符号必须完全匹配,一个符号错就解析失败; logstash 控制台 stdout 输出可以看到原始 message 和解析后的字段,用于调试 grok。
- Feign 调用后 traceId 丢失
- Feign 拦截器是否实例化成功;
- 下游服务 TraceFilter 是否生效,请求头
traceId是否透传。
- ES 磁盘爆满
- 设置索引生命周期策略,7‑14 天自动删除旧日志;测试环境不要长期保留日志。
- 生产环境注意事项
- ES 必须开启账号密码,不能裸奔;
- Logstash 增加队列配置,防止消息积压丢失;
- 高并发场景,Filebeat 输出到 kafka 做缓冲,再消费给 logstash,避免日志高峰压垮 ELK;
- 禁止业务服务直接输出日志到 logstash,必须 filebeat 做采集层解耦。
六、生产架构升级思路
测试环境:微服务 → Filebeat → Logstash → ES → Kibana 生产高可靠架构:微服务 → Filebeat → Kafka消息队列 → Logstash消费Kafka → ES → Kibana 增加 Kafka 削峰,防止突发大量日志打崩日志系统。
总结
搭建分布式日志环境,不只是把 ELK 部署起来。 关键点分为三块:
- 业务代码层:MDC 生成、透传 TraceId,统一日志输出格式;
- 采集层:Filebeat 轻量采集,避免侵入业务服务;
- 存储检索层:Logstash 解析字段存入 ES,Kibana 按 traceId 检索全链路日志。