SpringBoot自帶Controller接口監控,趕緊用起來
環境: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) ;
}
}
這樣我們就可以持久化存儲數據了,你還可以根據當前日期來進行存儲。