环境:SpringBoot2.7.18
1. 简介
项目中监控记录接口请求的相关信息是一个至关重要的环节,它对于提升系统稳定性、优化性能、快速定位问题以及保障数据安全等方面都起着至关重要的作用。大致可概况如下几方面:
问题追踪与定位:当系统出现错误或异常时,通过查看接口调用的请求信息,可以快速定位问题发生的源头。比如,通过查看请求参数、响应状态码、执行时间等
性能优化:监控接口请求的处理时间、响应时间性能指标,可以帮助开发团队了解系统的瓶颈所在,从而进行相应的优化。
用户行为分析:通过记录用户的请求信息,包括请求频率、请求时间、请求参数等,可以对用户行为进行分析,了解用户的使用习惯和需求,从而优化产品功能和用户体验。
安全审计:记录接口请求信息也是安全审计的一部分。通过监控和分析请求数据,可以发现潜在的安全威胁,如恶意请求、SQL注入、跨站脚本攻击等
在SpringBoot中我们可通过Actuator来实现对Http接口进行监控记录,接下来我们通过实操来演示如何通过Actuator来监控记录我们的即可。
2. 实战案例
2.1 引入依赖&配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
要使用Actuator的http接口监控功能,你还需要注册一个HttpTraceRepository类型的Bean,Actuator默认提供的是基于内存的实现
@Configuration
public class HttpActuatorConfig {
@Bean
InMemoryHttpTraceRepository inMemoryHttpTraceRepository() {
return new InMemoryHttpTraceRepository() ;
}
}
默认情况下,Actuator只开启了health接口(健康检查),我们还需要手动开启httptrace接口。
management:
endpoints:
web:
exposure:
include: httptrace
以上配置后,可以通过/actuator接口查看是否开启了httptrace接口
成功开启
2.2 测试接口
编写几个测试用的接口
@RestController
@RequestMapping("/users")
public class UserController {
private static List<User> DATAS = new ArrayList<>() ;
static {
DATAS.add(new User(1L, "张三", 22)) ;
DATAS.add(new User(2L, "李四", 32)) ;
DATAS.add(new User(3L, "王五", 33)) ;
DATAS.add(new User(4L, "赵六", 26)) ;
DATAS.add(new User(5L, "田七", 29)) ;
DATAS.add(new User(6L, "嘿哈", 44)) ;
}
@PostMapping("")
public ResponseEntity<Void> save(@RequestBody User user) {
DATAS.add(user) ;
return ResponseEntity.created(URI.create(String.format("/users/%s", user.getId()))).build() ;
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable("id") Long id) {
DATAS.removeIf(user -> user.getId() == id) ;
return ResponseEntity.noContent().build() ;
}
@PutMapping("")
public ResponseEntity<Void> update(@RequestBody User user) {
DATAS.stream()
.filter(u -> u.getId() == user.getId())
.findFirst()
.ifPresent(u -> {
u.setAge(user.getAge()) ;
u.setName(u.getName()) ;
});
return ResponseEntity.noContent().build() ;
}
@GetMapping("")
public ResponseEntity<List<User>> list() {
return ResponseEntity.ok(DATAS) ;
}
@GetMapping("/{id}")
public ResponseEntity<User> get(@PathVariable("id") Long id) {
return ResponseEntity.ok(DATAS.stream()
.filter(u -> u.getId() == id)
.findFirst().orElse(null)
) ;
}
// 测试异常情况
@GetMapping("/exception")
public ResponseEntity<Void> exce() {
System.out.println(1 / 0) ;
return ResponseEntity.noContent().build() ;
}
}
为了简单,上面操作都是基于内存数据进行。包括了CRUD及异常情况。
2.3 记录接口访问情况
访问上面定义的任意接口之后通过/actuator/httptrace接口查看访问情况
图片
每个接口会详细的记录请求的URL,header,响应状态码及header信息。上图中的timeTaken属性记录的是该接口请求的耗时情况(单位:毫秒)。
通过以上的示例演示,使用Actuator监控接口还是非常简单的,你只需要做简单的配置即可,接下来继续介绍更多的配置及使用。
2.4 自定义记录信息
要自定义记录的信息,可以通过如下配置
management:
trace:
http:
include:
- time-taken
- response-headers
注:在SpringBoot3以上的版本这里的配置发生了变化使用的是如下配置:
management:
httpexchanges:
recording:
include:
- time-taken
通过上面的配置后,再次访问接口
图片
此时,将不再包含请求headers。还支持如下的配置:
图片
2.5 手动获取请求响应信息
在项目中你可以通过 HttpTraceRepository 获取所有请求中的请求-响应交换信息。如下示例,获取所有错误的请求(状态码为500)。
private final HttpTraceRepository httpTraceRepository ;
public HttpController(HttpTraceRepository httpTraceRepository) {
this.httpTraceRepository = httpTraceRepository ;
}
@GetMapping("/{status}")
public Object info(@PathVariable("status") Integer status) {
return httpTraceRepository.findAll()
.stream()
.filter(trace -> trace.getResponse().getStatus() == status)
.collect(Collectors.toList()) ;
}
通过上面的接口,我们可以过滤指定状态码的请求信息。
2.6 自定义存储方式
InMemoryHttpExchangeRepository,默认情况下,它会存储最近 100 次请求-响应信息,并且是内存级的。所以如果你需要在生产环境下使用还是建议你自定义HttpTraceRepository实现,将信息存入到Redis或者是ES中。如下存入Redis示例
@Component
public class PackHttpTraceRepository implements HttpTraceRepository {
private static final String HTTP_TRACE_KEY = "http_request_response" ;
private final StringRedisTemplate stringRedisTemplate ;
public PackHttpTraceRepository(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate ;
}
@Override
public List<HttpTrace> findAll() {
return this.stringRedisTemplate.opsForList().range(HTTP_TRACE_KEY, 0, -1).stream().map(json -> {
try {
return objectMapper.readValue(json, HttpTrace.class);
}
}).collect(Collectors.toList()) ;
}
@Override
public void add(HttpTrace trace) {
String json = null ;
try {
json = objectMapper.writeValueAsString(trace) ;
}
this.stringRedisTemplate.opsForList().leftPush(HTTP_TRACE_KEY, json) ;
}
}
这样我们就可以持久化存储数据了,你还可以根据当前日期来进行存储。