SpringCloud微服务架构下的日志可观测解决方案(EFK搭建)
微服务架构日志采集及可视化平台搭建详解(EFK框架:ElasticSearch、Fluentd、kibana)
简介
在微服务架构下,服务众多,日志分散,难以整合进行全局检索分析,日志平台提供了全局日志采集、传输、存储与检索功能。
常用的日志解决技术方案
1、ELK:ElasticSearch + Logstash + kibana
ElasticSearch是一个分布式分析搜索引擎,可以存储大量日志数据,快速搜索日志和聚合功能,实现大规模日志的高效处理
Logstash是一个日志采集、过滤、转发工具,可以从文件、网络、消息队列中采集日志并发送到ElasticSearch进行存储
Kibana是日志的可视化平台,从ElasticSearch获取日志数据,进行数据分析,提供了更富的日志分析工具
2、EFK :ElasticSearch + Fluentd + kibana
Fluentd是日志采集工具,从日志文件采集日志进行格式转换,并上传到ElasticSearch进行存储,EFK是对ELK的改进,Fluentd性能比Logstash更高,适合处理大量日志数据
3、 PLG Stack: Promtail + Loki + Grafana
Promtail 日志采集器(读取文件、输出到 Loki)
Loki 类似 Elasticsearch 的日志存储,但为日志优化(轻量级)
Grafana 可视化日志,结合 Prometheus 实现日志+监控统一视图
三种方案比较:
ELK和EFK作为日志可视化平台,功能强大,具有强大的日志索引能力,占用资源更高,性能高,适合大量数据。
PLG不支持复杂字段索引,但是部署更轻量,适合云原生、功能较为简单,处理数据量少。
下面详细介绍如何实现微服务日志可视化EFK
1、微服务日志输出可以输出到指定目录中
springboot默认采用logback日志框架,日志输出格式可以选择json或者按正则表达视式输出,不同格式当然需要配置不同解析器,这里我们可以先不管,主要准备好日志源文件。
输出json日志:
pom.xml
<dependency><groupId>net.logstash.logback</groupId><artifactId>logstash-logback-encoder</artifactId><version>7.4</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId
</dependency>
resource/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--debug 是否打印logback中的日志 scan 是否检测logback配置文件变化更新-->
<configuration scan="true" debug="false"><!--springProperty 是spring结合logback的新属性,会解析application.yml中的配置变量 :-logs 是默认值写法${BUILD-FOLDER:-logs} spring不支持--><springProperty scope="context" name="LOG_PATH" source="logging.file.path" defaultValue="logs/${spring.application.name}"/><springProperty scope="context" name="LOG_NAME" source="spring.application.name" defaultValue="app"/><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/${LOG_NAME}.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/${LOG_NAME}-%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>7</maxHistory></rollingPolicy><!-- JSON 编码 --><encoder class="net.logstash.logback.encoder.LogstashEncoder"><customFields>{"appName":"${LOG_NAME}"}</customFields></encoder></appender><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="net.logstash.logback.encoder.LogstashEncoder"/></appender><root level="INFO"><appender-ref ref="FILE"/><appender-ref ref="STDOUT"/></root></configuration>
java代码:
Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class);@AutowiredAccountTblMapper accountTblMapper;@Transactional //本地事务@Overridepublic void debit(String userId, int money) {logger.info("account -debit 扣减余额服务");// 扣减账户余额accountTblMapper.debit(userId,money);}
这里就可以拿到一个json格式日志文件了,fluentd解析json不需要额外配置,相对简单
{"@timestamp":"2025-06-09T10:45:45.1149641+08:00","@version":"1","message":"Find sentinel dashboard server list: []","logger_name":"com.alibaba.cloud.sentinel.endpoint.SentinelHealthIndicator","thread_name":"RMI TCP Connection(5)-192.168.56.1","level":"INFO","level_value":20000,"nacosConfigLogLevel":"info","logPath":"${user.home}/logs","logRetainCount":"7","LOG_NAME":"seata-account","LOG_PATH":"logs/${spring.application.name}","logFileSize":"10MB","appName":"seata-account"}
{"@timestamp":"2025-06-09T10:48:50.9341798+08:00","@version":"1","message":"Initializing Spring DispatcherServlet 'dispatcherServlet'","logger_name":"org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/]","thread_name":"http-nio-10001-exec-1","level":"INFO","level_value":20000,"nacosConfigLogLevel":"info","logPath":"${user.home}/logs","logRetainCount":"7","LOG_NAME":"seata-account","LOG_PATH":"logs/${spring.application.name}","logFileSize":"10MB","appName":"seata-account"}
{"@timestamp":"2025-06-09T10:48:50.9351812+08:00","@version":"1","message":"Initializing Servlet 'dispatcherServlet'","logger_name":"org.springframework.web.servlet.DispatcherServlet","thread_name":"http-nio-10001-exec-1","level":"INFO","level_value":20000,"nacosConfigLogLevel":"info","logPath":"${user.home}/logs","logRetainCount":"7","LOG_NAME":"seata-account","LOG_PATH":"logs/${spring.application.name}","logFileSize":"10MB","appName":"seata-account"}
很多时候我们的日志格式并不是json,以文本格式存储的,如:
[2024-11-29 09:59:41.452] [] [com.example.order.Application] INFO 55: Starting Application using Java 1.8.0_361 on LAPTOP-TOOUJOQT with PID 35752 (E:\AiTrade2\KOCA_OCDES\aitrade\target\classes started by kxh in E:\AiTrade2\KOCA_OCDES\aitrade)
这种时候就要针对这种正则表达式进行解析。多种格式日志也没关系,fluentd支持配置多个日志源采集,只要配置解析器就可以。
比如:json格式日志输出到一个目录,text格式日志输出到一个目录
还有一种情况,日志分布在不同宿主机器上,这个时候可能需要在每一个节点部署一个fluentd代理采集日志发送到EL中心
2、日志源准备好了,开始采集吧
fluentd服务部署,部署方案有两种:
- Linux服务器本地部署
- Docker部署
我们这里介绍docker部署,机器配置2核2G足够,用到了docker-compose构建
efk-docker/├── docker-compose.yml│── flutend/├──conf│ ──fluent.conf│ ──Dockerfile
Dockerfile需要引入插件:
[root@master2 fluentd]# cat Dockerfile
FROM fluent/fluentd:v1.16-1USER root# 安装 elasticsearch 插件
RUN gem install fluent-plugin-elasticsearch --no-documentUSER fluent
docker-compose.yml:
[root@master2 efk-docker]# cat docker-compose.yml
services:fluentd:#image: fluent/fluentd:v1.16-1build:context: ./fluentdcontainer_name: fluentdvolumes:- ./fluentd/conf:/fluentd/etc- /var/log:/var/log- /opt/log:/opt/logports:- "24224:24224"- "24224:24224/udp"
# depends_on:# - elasticsearchnetworks:- efk-netnetworks:efk-net:driver: bridge[root@master2 efk-docker]#
fluentd配置文件需要配置采集日志源,输出目标地址ELSearch,配置文件我们放在构建路径下,/opt/efk-docker/fluentd/conf/fluent.conf
[root@master2 efk-docker]# cat ./fluentd/fluent.conf
<source>@type tailpath /opt/log/myapp/*.log # 监控的日志文件路径pos_file /fluentd/log/myapp.log.pos # 记录读取位置的文件,防止重复读tag myapp.log # 产生事件的标签format json # 直接指定格式为json,tail插件内置支持read_from_head true # 启动时从文件开头开始读取
</source># 非json日志
<source>@type tailpath /opt/log/aitrade/*.log # 监控的日志文件路径pos_file /fluentd/log/aitrade.log.pos # 记录读取位置的文件,防止重复读tag myapp.textlog # 产生事件的标签read_from_head true # 启动时从文件开头开始读取
<parse>
@type multilineformat_firstline /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]/format1 /^\[(?<time>[^\]]+)\] \[(?<trace_id>[^\]]*)\] \[(?<logger>[^\]]+)\] (?<level>\w+) (?<line>\d+): (?<message>.*)/time_format %Y-%m-%d %H:%M:%S.%L</parse>
</source><match myapp.**>@type copy # 多路输出<store>@type elasticsearch # 输出到 Elasticsearchhost 42.192.135.223port 9200scheme httplogstash_format true # 启用Logstash格式,生成时间戳索引logstash_prefix myapp-log # 索引前缀include_tag_key true # 包含tag字段reconnect_on_error truereload_on_failure true</store><store>@type stdout # 同时打印到标准输出,方便调试</store>
</match>
这里就配置好了,可以直接启动docker
docker compose up -d
查看fluentd采集日志
docker logs fluentd -f
3、部署ElsaticSearch
首先要部署的是ElsaticSearch,因为fluentd启动会去找el的地址,这个我们单独一台服务器部署,测试机器2核2G部署三个服务资源不足,另外找一台机器部署。
ELsearch和kinbana很容易部署的
[root@master1 efk-docker]# cat docker-compose.yml services:elasticsearch:image: 6541ba648813container_name: elasticsearchenvironment:- discovery.type=single-node- bootstrap.memory_lock=true- ES_JAVA_OPTS=-Xms512m -Xmx512mulimits:memlock:soft: -1hard: -1volumes:- esdata:/usr/share/elasticsearch/dataports:- "9200:9200"- "9300:9300"networks:- efk-netkibana:image: docker.elastic.co/kibana/kibana:7.17.17container_name: kibanaenvironment:- ELASTICSEARCH_HOSTS=http://elasticsearch:9200depends_on:- elasticsearchports:- "5601:5601"networks:- efk-net
volumes:esdata:networks:efk-net:driver: bridge[root@master1 efk-docker]#
直接启动这个docker compose就好了
启动好了后,前端访问http://42.192.133.32:5601,ip为kinbana宿主机地址,就可以进入kinbana页面了。
4、前端访问
kinbana还看不到日志数据,需要创建索引,进入页面就会提示Create Index Pattern 进入创建 ,name填写之前配置的( logstash_prefix myapp-log # 索引前缀) ,这里填myapp-log*,另外一个空选择@timestamp即可,回到日志页面就可以看到我们的日志信息了