成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

全自動!Spring Boot 實現接口請求/響應日志記錄的高效方案

開發 前端
在當今微服務體系中,記錄接口調用日志不僅是排查問題的關鍵手段,也是保障系統合規與可審計的重要一環。然而傳統做法往往需要在攔截器或過濾器中編寫大量樣板代碼,以實現請求體緩存、響應內容重復讀取等功能,既繁瑣又容易遺漏。

在當今微服務體系中,記錄接口調用日志不僅是排查問題的關鍵手段,也是保障系統合規與可審計的重要一環。然而傳統做法往往需要在攔截器或過濾器中編寫大量樣板代碼,以實現請求體緩存、響應內容重復讀取等功能,既繁瑣又容易遺漏。

好在 Spring Boot 已經提供了更現代化的方案 —— 利用 Actuator 內置功能,可以快速集成請求追蹤機制。本文將完整演示如何構建輕量、可擴展的 API 調用日志系統。

背景說明

在微服務網關、后端服務、BFF 層等多個場景下,開發者都需要了解:

  • 用戶請求了什么接口?
  • 響應結果是否正常?
  • 調用耗時多少?
  • 有沒有異常發生?

Spring Boot Actuator 提供了 /actuator/httptrace 或 /ac/httpexchanges(自定義路徑)等端點,可以追蹤最近的 HTTP 請求,但默認并不記錄請求體、響應體等關鍵內容。

接下來我們就基于 Spring Boot + Actuator 構建一套 API 日志系統,包含:

  • 基于內存的請求記錄方案;
  • 自定義持久化日志記錄方案(如 Redis);
  • 自定義日志數據結構 HttpLog;
  • 完整示例代碼與調用效果展示。

實戰部署

添加依賴模塊

在 pom.xml 中引入 Actuator 核心依賴:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

啟用配置項

在 application.yml 中啟用 Actuator 的 HTTP 請求跟蹤端點:

management:
  endpoints:
    web:
      base-path: /ac
  httpexchanges:
    recording:
      enabled: true

這一步允許通過 /ac/httpexchanges 查詢接口調用情況,但要啟用實際記錄,還需要注冊 Repository Bean。

啟用請求記錄組件

內存版 Repository 配置

創建類 HttpTraceConfig.java:

package com.icoderoad.logtrace.config;


import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class HttpTraceConfig {


    @Bean
    public InMemoryHttpExchangeRepository httpExchangeRepository() {
        InMemoryHttpExchangeRepository repository = new InMemoryHttpExchangeRepository();
        repository.setCapacity(20); // 默認100條,這里改為20條
        repository.setReverse(true); // 最新記錄排在前面
        return repository;
    }
}

此配置用于在內存中保存最近的請求記錄,適合調試使用。

模擬接口請求測試

創建接口 ApiController.java:

package com.icoderoad.logtrace.controller;


import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;


@RestController
@RequestMapping("/api")
public class ApiController {


    @GetMapping("/{id}")
    public ResponseEntity<User> query(@PathVariable Long id) {
        return ResponseEntity.ok(new User(id, "姓名 - " + id));
    }


    @GetMapping
    public ResponseEntity<List<User>> list(@RequestParam String name) {
        return ResponseEntity.ok(List.of(
            new User(1L, name + " - 1"),
            new User(2L, name + " - 2")
        ));
    }


    @GetMapping("/s/{type}")
    public ResponseEntity<String> s(@PathVariable String type, @RequestParam String name) {
        return ResponseEntity.ok(String.format("type: %s, name: %s", type, name));
    }


    public record User(Long id, String name) {}
}

通過訪問 /api/1、/api?name=test、/api/s/debug?name=test 等接口后,可訪問 /ac/httpexchanges 查看請求元數據(請求地址、狀態碼、耗時等)。

自定義持久化日志(Redis)

內存方案不適合生產系統,我們需要把日志存儲到 Redis 等持久介質中。

 Redis 日志實現類

路徑:RedisHttpExchangeRepository.java

package com.icoderoad.logtrace.repository;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.boot.actuate.web.exchanges.HttpExchange;
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import java.time.ZoneId;
import java.util.List;


@Component
public class RedisHttpExchangeRepository implements HttpExchangeRepository {


    private final StringRedisTemplate redis;
    private final ObjectMapper objectMapper;


    public RedisHttpExchangeRepository(StringRedisTemplate redis, ObjectMapper objectMapper) {
        this.redis = redis;
        this.objectMapper = objectMapper;
    }


    @Override
    public void add(HttpExchange exchange) {
        try {
            HttpLog log = new HttpLog();
            log.setTimestamp(exchange.getTimestamp().atZone(ZoneId.systemDefault()).toLocalDateTime());
            log.setTimeTaken(exchange.getTimeTaken());
            log.setPrincipal(exchange.getPrincipal());


            HttpLog.Request req = new HttpLog.Request();
            BeanUtils.copyProperties(exchange.getRequest(), req);
            log.setRequest(req);


            HttpLog.Response resp = new HttpLog.Response();
            BeanUtils.copyProperties(exchange.getResponse(), resp);
            log.setResponse(resp);


            redis.opsForList().leftPush("http:request:list", objectMapper.writeValueAsString(log));
        } catch (JsonProcessingException e) {
            // ignore
        }
    }


    @Override
    public List<HttpExchange> findAll() {
        List<String> rawList = redis.opsForList().range("http:request:list", 0, -1);
        return rawList.stream().map(record -> {
            try {
                HttpLog log = objectMapper.readValue(record, HttpLog.class);
                HttpExchange.Request request = new HttpExchange.Request(
                    log.getRequest().getUri(),
                    log.getRequest().getRemoteAddress(),
                    log.getRequest().getMethod(),
                    log.getRequest().getHeaders()
                );
                HttpExchange.Response response = new HttpExchange.Response(
                    log.getResponse().getStatus(),
                    log.getResponse().getHeaders()
                );
                return new HttpExchange(
                    log.getTimestamp().atZone(ZoneId.systemDefault()).toInstant(),
                    request, response, log.getPrincipal(), null, log.getTimeTaken()
                );
            } catch (Exception e) {
                return null;
            }
        }).filter(e -> e != null).toList();
    }
}

日志結構對象定義

路徑:HttpLog.java

package com.icoderoad.logtrace.model;


import java.net.URI;
import java.security.Principal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;


public class HttpLog {
    private LocalDateTime timestamp;
    private Request request;
    private Response response;
    private Principal principal;
    private Duration timeTaken;


    public static class Request {
        private URI uri;
        private String remoteAddress;
        private String method;
        private Map<String, List<String>> headers;


        // Getters and Setters
    }


    public static class Response {
        private int status;
        private Map<String, List<String>> headers;


        // Getters and Setters
    }


    // Getters and Setters for HttpLog
}

 總結

盡管 /ac/httpexchanges 接口能快速調試查看請求歷史,但它提供的數據相對有限,無法滿足生產需求。

如果你希望:

  • 記錄完整請求體/響應體;
  • 保存歷史數據供 ELK/SLS 分析;
  • 接入自定義日志分析服務;
責任編輯:武曉燕 來源: 路條編程
相關推薦

2021-08-11 05:00:48

Spring 日志手段

2025-01-08 09:35:55

Spring性能監控

2025-02-03 09:00:00

API接口性能

2022-02-08 17:07:54

Spring BooSpring Aop日志記錄

2021-03-01 23:26:41

日志Spring BootAOP

2025-02-05 12:28:44

2024-08-01 09:10:03

2024-06-04 10:05:48

微服務網關日志

2024-08-29 09:01:39

2025-03-31 08:39:55

2025-01-08 10:35:26

代碼開發者Spring

2018-11-19 14:29:17

Spring BootXML支持

2022-01-05 08:29:22

監控Prometheus Post

2019-04-15 08:32:25

Spring Boot日志門面模式

2024-12-18 12:10:00

2025-02-12 08:07:40

2024-10-18 08:00:00

SpringBoot框架開發

2025-06-06 08:28:56

2025-05-27 07:07:29

2025-05-14 04:00:00

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费一区二区 | 欧美午夜精品久久久久免费视 | 北条麻妃国产九九九精品小说 | 午夜小电影 | 国产在线观看一区二区 | 91 久久 | 亚洲视频自拍 | 亚洲精品粉嫩美女一区 | 91一区| 一区二区三区四区av | 色就干| 精品国产乱码久久久久久蜜臀 | 精品久久久久久久久久久久久 | a天堂在线| 日韩一区二区黄色片 | 超碰97av | 国产精品成av人在线视午夜片 | 国产精品视频www | 精品国产欧美一区二区三区成人 | 全免费a级毛片免费看视频免费下 | 中文字幕视频一区 | 日韩欧美在线视频 | 日韩中文字幕一区 | 伊人久久麻豆 | 一区二区三区国产精品 | 日韩成人av在线播放 | 天天操操操操操 | 国产精品久久久久久久久久久久久 | 伊人色综合久久天天五月婷 | 蜜桃在线视频 | 伊人久久成人 | 欧美亚洲免费 | 精品一区二区三区91 | 欧美激情综合 | 最新91在线 | 一区二区三区高清 | 亚洲图片一区二区三区 | 成人在线视频网站 | a毛片视频网站 | 国产欧美一区二区精品久导航 | 久草在线高清 |