偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

全自動(dòng)!Spring Boot 實(shí)現(xiàn)接口請(qǐng)求/響應(yīng)日志記錄的高效方案

開發(fā) 前端
在當(dāng)今微服務(wù)體系中,記錄接口調(diào)用日志不僅是排查問題的關(guān)鍵手段,也是保障系統(tǒng)合規(guī)與可審計(jì)的重要一環(huán)。然而傳統(tǒng)做法往往需要在攔截器或過濾器中編寫大量樣板代碼,以實(shí)現(xiàn)請(qǐng)求體緩存、響應(yīng)內(nèi)容重復(fù)讀取等功能,既繁瑣又容易遺漏。

在當(dāng)今微服務(wù)體系中,記錄接口調(diào)用日志不僅是排查問題的關(guān)鍵手段,也是保障系統(tǒng)合規(guī)與可審計(jì)的重要一環(huán)。然而傳統(tǒng)做法往往需要在攔截器或過濾器中編寫大量樣板代碼,以實(shí)現(xiàn)請(qǐng)求體緩存、響應(yīng)內(nèi)容重復(fù)讀取等功能,既繁瑣又容易遺漏。

好在 Spring Boot 已經(jīng)提供了更現(xiàn)代化的方案 —— 利用 Actuator 內(nèi)置功能,可以快速集成請(qǐng)求追蹤機(jī)制。本文將完整演示如何構(gòu)建輕量、可擴(kuò)展的 API 調(diào)用日志系統(tǒng)。

背景說明

在微服務(wù)網(wǎng)關(guān)、后端服務(wù)、BFF 層等多個(gè)場(chǎng)景下,開發(fā)者都需要了解:

  • 用戶請(qǐng)求了什么接口?
  • 響應(yīng)結(jié)果是否正常?
  • 調(diào)用耗時(shí)多少?
  • 有沒有異常發(fā)生?

Spring Boot Actuator 提供了 /actuator/httptrace 或 /ac/httpexchanges(自定義路徑)等端點(diǎn),可以追蹤最近的 HTTP 請(qǐng)求,但默認(rèn)并不記錄請(qǐng)求體、響應(yīng)體等關(guān)鍵內(nèi)容。

接下來我們就基于 Spring Boot + Actuator 構(gòu)建一套 API 日志系統(tǒng),包含:

  • 基于內(nèi)存的請(qǐng)求記錄方案;
  • 自定義持久化日志記錄方案(如 Redis);
  • 自定義日志數(shù)據(jù)結(jié)構(gòu) HttpLog;
  • 完整示例代碼與調(diào)用效果展示。

實(shí)戰(zhàn)部署

添加依賴模塊

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

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

啟用配置項(xiàng)

在 application.yml 中啟用 Actuator 的 HTTP 請(qǐng)求跟蹤端點(diǎn):

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

這一步允許通過 /ac/httpexchanges 查詢接口調(diào)用情況,但要啟用實(shí)際記錄,還需要注冊(cè) Repository Bean。

啟用請(qǐng)求記錄組件

內(nèi)存版 Repository 配置

創(chuàng)建類 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); // 默認(rèn)100條,這里改為20條
        repository.setReverse(true); // 最新記錄排在前面
        return repository;
    }
}

此配置用于在內(nèi)存中保存最近的請(qǐng)求記錄,適合調(diào)試使用。

模擬接口請(qǐng)求測(cè)試

創(chuàng)建接口 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 查看請(qǐng)求元數(shù)據(jù)(請(qǐng)求地址、狀態(tài)碼、耗時(shí)等)。

自定義持久化日志(Redis)

內(nèi)存方案不適合生產(chǎn)系統(tǒng),我們需要把日志存儲(chǔ)到 Redis 等持久介質(zhì)中。

 Redis 日志實(shí)現(xiàn)類

路徑: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();
    }
}

日志結(jié)構(gòu)對(duì)象定義

路徑: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
}

 總結(jié)

盡管 /ac/httpexchanges 接口能快速調(diào)試查看請(qǐng)求歷史,但它提供的數(shù)據(jù)相對(duì)有限,無法滿足生產(chǎn)需求。

如果你希望:

  • 記錄完整請(qǐng)求體/響應(yīng)體;
  • 保存歷史數(shù)據(jù)供 ELK/SLS 分析;
  • 接入自定義日志分析服務(wù);
責(zé)任編輯:武曉燕 來源: 路條編程
相關(guān)推薦

2021-08-11 05:00:48

Spring 日志手段

2025-01-08 09:35:55

Spring性能監(jiān)控

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

2025-06-30 01:45:00

2024-08-01 09:10:03

2024-08-29 09:01:39

2024-06-04 10:05:48

微服務(wù)網(wǎng)關(guān)日志

2025-03-31 08:39:55

2025-01-08 10:35:26

代碼開發(fā)者Spring

2018-11-19 14:29:17

Spring BootXML支持

2022-01-05 08:29:22

監(jiān)控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框架開發(fā)

2025-05-27 07:07:29

2025-06-06 08:28:56

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)