目录
[3 Spring Cloud+ELK+AOP](#3 Spring Cloud+ELK+AOP)
[3.1 整体架构和调用流程](#3.1 整体架构和调用流程)
[3.2 具体代码](#3.2 具体代码)
[3.2.1 ELK](#3.2.1 ELK)
[3.2.2 生成者项目](#3.2.2 生成者项目)
[3.2.3 消费者项目](#3.2.3 消费者项目)
[3.2.4 运行](#3.2.4 运行)
[3.3 面试题](#3.3 面试题)
3 Spring Cloud+ELK+AOP
使用Spring Cloud+ELK+AOP搭建一个简单的项目
3.1 整体架构和调用流程
整体架构总览
- 生产者服务 cloud-provider:端口 8081,提供接口服务
- 消费者服务 cloud-consumer:端口 8082,通过 OpenFeign 远程调用生产者
- AOP 切面:拦截消费者所有接口,打印请求、返回日志
- ELK 整套环境:Logback → Logstash → Elasticsearch → Kibana 存储展示日志
调用流程
浏览器
↓
消费者Controller接口
↓
AOP切面拦截(打印请求日志)
↓
Feign远程调用
↓
生产者Controller接口(处理业务)
↓
生产者返回数据
↓
Feign链路回传给消费者
↓
AOP切面打印返回日志
↓
Logback日志框架
↓
Logstash
↓
Elasticsearch存储
↓
Kibana查询展示
-
启动环境 首先启动本地 Docker 中的 ELK 全套服务 (Elasticsearch、Logstash、Kibana),Logstash 监听本地 4560 端口等待接收日志。随后依次在 IDEA 中启动生产者微服务 、消费者微服务。
-
浏览器发起请求用户浏览器访问消费者接口地址:
请求进入消费者项目的 ConsumerController 的 call() 接口方法。
-
AOP 切面拦截请求
消费者项目的 AOP 环绕切面 拦截该接口请求,执行前置日志打印,记录接口方法名、请求参数信息。 -
消费者通过 Feign 远程调用生产者
消费者内部通过 @FeignClient 声明的远程接口, 向本地 8081 端口的生产者服务 发起 HTTP 请求,调用生产者的 /provider/hello 接口。 -
生产者接收并处理请求
生产者服务接收到消费者的远程调用请求,执行自身接口业务,返回结果字符串:我是生产者,你调用成功啦!
-
结果回传给消费者
生产者将接口返回数据,通过 Feign 调用链路原路返回给消费者。 -
消费者拼接结果并响应浏览器
消费者拿到生产者返回数据,拼接返回内容,最终响应给浏览器:消费者调用生产者:我是生产者,你调用成功啦!
-
AOP 后置日志打印
接口方法执行完毕后,AOP 切面继续执行,打印接口最终返回结果,完整日志输出到 IDEA 控制台。 -
日志发送至 ELK
项目通过 logback-spring.xml 日志配置,将 AOP 打印的所有日志,发送到本地 4560 端口的 Logstash。 -
日志存储与可视化查看
Logstash 接收日志后,将日志数据存入 Elasticsearch 搜索引擎;最后在 Kibana 可视化界面中,通过索引检索,即可查询到全部接口调用日志。
3.2 具体代码
3.2.1 ELK

logstash.conf
# 接收Spring传来的日志,转发到ES
input {
tcp {
port => 4560 # Spring 连接这个端口
codec => json_lines
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "spring-log-%{+YYYY.MM.dd}" # 日志存储的索引名
}
}
docker-compose.yml
# 一键启动 ELK 脚本
version: '3'
services:
# 搜索引擎:存储日志
elasticsearch:
image: elasticsearch:7.17.0
container_name: es
ports:
- "9200:9200"
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m # 限制内存,不卡电脑
# 日志收集:接收Spring传来的日志
logstash:
image: logstash:7.17.0
container_name: logstash
ports:
- "4560:4560"
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
depends_on:
- elasticsearch
# 界面:查看日志
kibana:
image: kibana:7.17.0
container_name: kibana
ports:
- "5601:5601"
depends_on:
- elasticsearch
运行

成功后访问浏览器,Kibana 界面:http://localhost:5601,出现下面这个页面就是成功

3.2.2 生成者项目
创建SpringBoot项目cloud-provider。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>cloud-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud-provider</name>
<description>cloud-provider</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AOP依赖:实现切面统一日志功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 日志发送依赖:把Spring日志发送到本地Logstash(你的ELK环境) -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
# 生产者服务端口
server:
port: 8081
# 服务名称(微服务标识,随便写)
spring:
application:
name: service-provider
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 发送日志到 ELK Logstash -->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>localhost:4560</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="LOGSTASH"/>
</root>
</configuration>
package com.cloudprovider.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
// AOP 切面:自动拦截所有接口,打印日志
@Aspect
@Component
@Slf4j
public class LogAop {
// 拦截所有Controller接口
@Around("execution(* com.cloudprovider.controller..*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
// 打印请求方法名
log.info("【AOP】请求接口:{}", joinPoint.getSignature().getName());
// 执行原方法
Object result = joinPoint.proceed();
// 打印返回结果
log.info("【AOP】返回结果:{}", result);
return result;
}
}
package com.cloudprovider.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// 生产者接口
@RestController
@RequestMapping("/provider")
public class ProviderController {
// 提供给消费者调用的接口
@GetMapping("/hello")
public String hello() {
return "我是生产者,你调用成功啦!";
}
}
3.2.3 消费者项目
创建SpringBoot项目cloud-consumer
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>cloud-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud-consumer</name>
<description>cloud-consumer</description>
<properties>
<spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AOP 日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- OpenFeign 调用生产者 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 日志发送到 ELK -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<!-- Spring Cloud 依赖管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
# 消费者端口
server:
port: 8082
# 消费者服务名(标识作用,随便写)
spring:
application:
name: service-consumer
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 发送日志到 ELK Logstash -->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>localhost:4560</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="LOGSTASH"/>
</root>
</configuration>
package com.cloudconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients // 开启Feign调用
@SpringBootApplication
public class CloudConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(CloudConsumerApplication.class, args);
}
}
package com.cloudconsumer.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
// AOP 切面:自动拦截所有接口,打印日志
@Aspect
@Component
@Slf4j
public class LogAop {
// 拦截所有Controller接口
@Around("execution(* com.cloudconsumer.controller..*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
// 打印请求方法名
log.info("【AOP】请求接口:{}", joinPoint.getSignature().getName());
// 执行原方法
Object result = joinPoint.proceed();
// 打印返回结果
log.info("【AOP】返回结果:{}", result);
return result;
}
}
package com.cloudconsumer.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
// AOP 切面:自动拦截所有接口,打印日志
@Aspect
@Component
@Slf4j
public class LogAop {
// 拦截所有Controller接口
@Around("execution(* com.cloudconsumer.controller..*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
// 打印请求方法名
log.info("【AOP】请求接口:{}", joinPoint.getSignature().getName());
// 执行原方法
Object result = joinPoint.proceed();
// 打印返回结果
log.info("【AOP】返回结果:{}", result);
return result;
}
}
package com.cloudconsumer.controller;
import com.cloudconsumer.feign.ProviderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConsumerController {
// 注入Feign客户端
@Autowired
private ProviderFeign providerFeign;
// 消费者调用生产者
@GetMapping("/call")
public String call() {
return "消费者调用生产者:" + providerFeign.hello();
}
}
3.2.4 运行
依次运行ELK、生成者项目、消费者项目。
浏览器访问接口

查看日志,此外项目的控制台也有日志输出

3.3 面试题
