springboot配置請求日志

springboot配置請求日志

一般情況下,接口請求都需要日志記錄,Java springboot中的日志記錄相對復雜一點

經過實踐,以下方案可行,記錄一下完整過程

一、創建日志數據模型

創建實體類,也就是日志文件中要記錄的數據格式

我是放在我的實體類包中,也就算pojo包下

package org.example.pojo;import lombok.Data;@Data
public class RequestLog {private String url; // 請求URLprivate String method; // HTTP方法private long startTime; // 開始時間private long endTime; // 結束時間private String remoteIp; // 客戶端IPprivate String queryString; // 查詢參數private String requestBody; // 請求體(根據需求謹慎記錄)private Integer status; // 響應狀態碼private Long costTime; // 耗時(ms)@Overridepublic String toString(){return "{" +"\"url\":\"" + url + '\"' +", \"method\":\"" + method + '\"' +", \"remoteIp\":\"" + remoteIp + '\"' +", \"status\":" + status +", \"costTime\":" + costTime +", \"queryString\":\"" + (queryString != null ? queryString : "") + '\"' +", \"requestBody\":\"" + (requestBody != null ? requestBody : "") + '\"' +'}';}
}

這里有個小問題,requestBody似乎取不到,比較麻煩,但不影響記錄

二、創建攔截器

其實就是每次請求前,讀取請求信息,并添加到請求實體中去

我放在interceptor包下

package org.example.interceptor;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.pojo.RequestLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;@Component
public class RequestLoggingInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger("REQUEST_LOG");private static final String ATTRIBUTE_REQUEST_LOG = "requestLog";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {RequestLog requestLog = new RequestLog();requestLog.setStartTime(System.currentTimeMillis());requestLog.setUrl(request.getRequestURI());requestLog.setMethod(request.getMethod());// request請求中的原始iprequestLog.setRemoteIp(request.getRemoteHost());// 自定義的獲取ip方法,考慮了使用代理的情況
//        requestLog.setRemoteIp(getClientIp(request));requestLog.setQueryString(request.getQueryString());
//        下面這兩行會報錯
//        requestLog.setRequestBody(request.getReader().toString());
//        requestLog.setRequestBody(request.getRequestBody());request.setAttribute(ATTRIBUTE_REQUEST_LOG, requestLog);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,  Object handler, Exception ex) throws Exception {RequestLog requestLog = (RequestLog) request.getAttribute(ATTRIBUTE_REQUEST_LOG);if (requestLog != null) {requestLog.setEndTime(System.currentTimeMillis());requestLog.setCostTime(System.currentTimeMillis() - requestLog.getStartTime());requestLog.setStatus(response.getStatus());logger.info(requestLog.toString());}}/*** 獲取客戶端真實IP(考慮了代理情況)*/private String getClientIp(HttpServletRequest request) {String ip = request.getHeader("X-Forwarded-For");if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}
}

幾個基礎問題記錄下:

  • Component注解,將這個方法注入到springboot項目中,使其成為項目組件
  • preHandle和afterCompletion都是HandlerInterceptor中原有的方法,重寫方法時,必須保證參數完全一致,同時需要拋出異常
  • slf4j在maven中并沒有顯示引入,應該是springboot自帶的

三、注冊攔截器

需要寫一個配置類,我放在config包中

package org.example.config;import org.example.interceptor.RequestLoggingInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate RequestLoggingInterceptor requestLoggingInterceptor;@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://localhost:5173", "http://localhost:5174").allowedMethods("*").allowedHeaders("*").allowCredentials(true).maxAge(3600);}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(requestLoggingInterceptor).addPathPatterns("/**");  // 攔截所有請求路徑,都需要添加日志}
}

幾點說明:

  • addCorsMappings用來處理跨域,重寫父類方法
  • 攔截器在addInterceptors,也是重寫的

四、配置 Logback 以將日志保存到文件

src/main/resources目錄下創建或修改 logback-spring.xml文件,為請求日志配置一個獨立的追加器 (Appender),并將其輸出到文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 定義日志文件存儲路徑和文件名 --><property name="LOG_PATH" value="./logs" /><property name="REQUEST_LOG_FILE" value="${LOG_PATH}/request.log" /><!-- 控制臺輸出配置(可選) --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- 為請求日志配置獨立的滾動文件Appender --><appender name="REQUEST_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${REQUEST_LOG_FILE}</file> <!-- 當前活動的日志文件 --><encoder><!-- 配置輸出格式,因為我們在Interceptor中已輸出JSON字符串,這里只需消息本身 --><pattern>%msg%n</pattern> </encoder><!-- 滾動策略:按日期和大小滾動 --><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 滾動后的文件命名模式:按天歸檔,超過大小則遞增,并自動壓縮 --><fileNamePattern>${REQUEST_LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern><maxFileSize>50MB</maxFileSize> <!-- 每個文件最大大小 --><maxHistory>30</maxHistory> <!-- 保留30天的歷史日志 --><totalSizeCap>5GB</totalSizeCap> <!-- 所有請求日志文件總大小上限 --></rollingPolicy></appender><!-- 專門為我們的請求日志logger配置,指向獨立的文件Appender,并不再向上傳遞(additivity=false) --><logger name="REQUEST_LOG" level="INFO" additivity="false"><appender-ref ref="REQUEST_FILE" /><!-- 如果需要同時在控制臺看到,可以加上 <appender-ref ref="CONSOLE"/> --></logger><!-- 根日志記錄器(其他日志的輸出配置) --><root level="INFO"><appender-ref ref="CONSOLE" /></root>
</configuration>

五、日志文件

{"url":"/api/layout/positions", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":200, "costTime":310, "queryString":""}
{"url":"/api/user/login", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":200, "costTime":19, "queryString":""}
{"url":"/api/user/login", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":200, "costTime":10, "queryString":"", "requestBody":"{"}
{"url":"/error", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":500, "costTime":27, "queryString":"", "requestBody":"    "username": "huangg","}
{"url":"/api/user/login", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":200, "costTime":283, "queryString":"", "requestBody":""}
{"url":"/api/user/login", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":200, "costTime":10, "queryString":"", "requestBody":"org.apache.catalina.connector.CoyoteReader@6906fda1"}
{"url":"/error", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":500, "costTime":27, "queryString":"", "requestBody":"org.apache.catalina.connector.CoyoteReader@6906fda1"}
{"url":"/api/user/login", "method":"POST", "remoteIp":"0:0:0:0:0:0:0:1", "status":200, "costTime":301, "queryString":"", "requestBody":""}
{"url":"/api/user/login", "method":"POST", "remoteIp":"127.0.0.1", "status":200, "costTime":3, "queryString":"", "requestBody":""}
{"url":"/api/user/login", "method":"POST", "remoteIp":"192.168.0.94", "status":200, "costTime":4, "queryString":"", "requestBody":""}
  • 如果本機上發起請求,請求域名用的是localhost,則remoteIp是ipv6地址,0:0:0:0:0:0:0:1
  • 如果本機上發起請求,請求域名用的是127.0.0.1,則remoteIp是127.0.0.1
  • 如果使用真實ip地址192.168.0.94,則remoteIp是真實IP192.168.0.94

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96062.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96062.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96062.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis(50) Redis哨兵如何與客戶端進行交互?

Redis 哨兵&#xff08;Sentinel&#xff09;不僅負責監控和管理 Redis 主從復制集群的高可用性&#xff0c;還需要與客戶端進行有效的交互來實現故障轉移后的透明連接切換。下面詳細探討 Redis 哨兵如何與客戶端進行交互&#xff0c;并結合代碼示例加以說明。 哨兵與客戶端的交…

【.Net技術棧梳理】04-核心框架與運行時(線程處理)

文章目錄1. 線程管理1.1 線程的核心概念&#xff1a;System.Threading.Thread1.2 現代線程管理&#xff1a;System.Threading.Tasks.Task 和 Task Parallel Library (TPL)1.3 狀態管理和異常處理1.4 協調任務&#xff1a;async/await 模式2. 線程間通信2.1 共享內存與競態條件2…

(JVM)四種垃圾回收算法

在 JVM 中&#xff0c;垃圾回收&#xff08;GC&#xff09;是核心機制之一。為了提升性能與內存利用率&#xff0c;JVM 采用了多種垃圾回收算法。本文總結了 四種常見的 GC 算法&#xff0c;并結合其優缺點與應用場景進行說明。1. 標記-清除&#xff08;Mark-Sweep&#xff09;…

論文閱讀:VGGT Visual Geometry Grounded Transformer

論文閱讀&#xff1a;VGGT: Visual Geometry Grounded Transformer 今天介紹一篇 CVPR 2025 的 best paper&#xff0c;這篇文章是牛津大學的 VGG 團隊的工作&#xff0c;主要圍繞著 3D 視覺中的各種任務&#xff0c;這篇文章提出了一種多任務統一的架構&#xff0c;實現一次輸…

python編程:一文掌握pypiserver的詳細使用

更多內容請見: python3案例和總結-專欄介紹和目錄 文章目錄 一、 pypiserver 概述 1.1 pypiserver是什么? 1.2 核心特性 1.3 典型應用場景 1.4 pypiserver優缺點 二、 安裝與基本使用 2.1 安裝 pypiserver 2.2 快速啟動(最簡模式) 2.3 使用私有服務器安裝包 2.4 向私有服務…

Git reset 回退版本

- 第 121 篇 - Date: 2025 - 09 - 06 Author: 鄭龍浩&#xff08;仟墨&#xff09; 文章目錄Git reset 回退版本1 介紹三種命令區別3 驗證三種的區別3 如果不小心git reset --hard將「工作區」和「暫存區」中的內容刪除&#xff0c;剛才的記錄找不到了&#xff0c;怎么辦呢&…

ARM 基礎(2)

ARM內核工作模式及其切換條件用戶模式(User Mode, usr) 權限最低&#xff0c;運行普通應用程序。只能通過異常被動切換到其他模式。快速中斷模式(FIQ Mode, fiq) 處理高速外設中斷&#xff0c;專用寄存器減少上下文保存時間&#xff0c;響應周期約4個時鐘周期。觸發條件為FIQ中…

Flutter 性能優化

Flutter 性能優化是一個系統性的工程&#xff0c;涉及多個層面。 一、性能分析工具&#xff08;Profiling Tools&#xff09; 在開始優化前&#xff0c;必須使用工具定位瓶頸。切忌盲目優化。 1. DevTools 性能視圖 DevTools 性能視圖 (Performance View) 作用&#xff1a;…

Spring事件監聽機制(三)

為了理解EvenListener注解的底層原理&#xff0c;我們可以自己實現一個類似的注解模擬實現。1.定義MyListener注解Target({ElementType.METHOD})Retention(RetentionPolicy.RUNTIME)public interface MyListener {}2.注解使用Componentstatic class SmsService {private static…

基于Springboot + vue3實現的小區物業管理系統

項目描述本系統包含管理員和用戶兩個角色。管理員角色&#xff1a;用戶管理&#xff1a;管理系統中所有用戶的信息&#xff0c;包括添加、刪除和修改用戶。房屋信息管理&#xff1a;管理房屋信息&#xff0c;包括新增、查看、修改和刪除房屋信息。車輛信息管理&#xff1a;管理…

交叉熵和KL散度

這個問題之前我也是傻傻分不清&#xff0c;決定整理一下&#xff0c;用更印象深刻的方式讓人記住。核心聯系&#xff1a;交叉熵 KL 散度 真實分布的熵 交叉熵作為 “絕對” 度量&#xff0c;會綜合真實分布的熵&#xff08;固有難度&#xff09;與預測誤差&#xff0c;直接體…

HTML 各種事件的使用說明書

HTML 各種事件的使用說明書 1. HTML 事件簡介 HTML事件是瀏覽器或用戶在網頁上執行的動作或發生的事情。當這些事件發生時&#xff0c;可以通過JavaScript來響應和處理這些事件&#xff0c;從而實現網頁的交互功能。事件處理是Web前端開發中實現動態交互的核心機制。 基本概…

Kafka面試精講 Day 10:事務機制與冪等性保證

【Kafka面試精講 Day 10】事務機制與冪等性保證 在分布式消息系統中&#xff0c;如何確保消息不丟失、不重復&#xff0c;是系統可靠性的核心挑戰。Kafka自0.11版本起引入了冪等性Producer和事務性消息機制&#xff0c;徹底解決了“至少一次”語義下可能產生的重復消息問題&am…

時序數據庫簡介和安裝

一、簡介1. 什么是時序數據庫&#xff1f;時序數據庫是專門用于存儲和處理時間序列數據的數據庫系統。時間序列數據是指按時間順序索引的一系列數據點。每個數據點都包含&#xff1a;一個時間戳&#xff1a;記錄數據產生的時間。一個或多個指標值&#xff1a;例如溫度、濕度、C…

comfyUI 暴露網絡restful http接口

https://zhuanlan.zhihu.com/p/686893291 暴露websocket接口。 打開開發者選項 如圖

linux系統address already in use問題解決

linux系統上某個端口被占用&#xff0c;如何解決&#xff1f;1.找到占用的進程編號&#xff1a;netstat -tulnp | grep :80002.強制殺死該進程kill -9 80603其他說明&#xff1a;1.查找占用端口的進程&#xff0c;可以用&#xff1a;lsof -i :8001 # 或者使用 netstat -tulnp |…

基于SpringBoot的家政保潔預約系統【計算機畢業設計選題 計算機畢業設計項目 計算機畢業論文題目推薦】

&#x1f34a;作者&#xff1a;計算機編程-吉哥 &#x1f34a;簡介&#xff1a;專業從事JavaWeb程序開發&#xff0c;微信小程序開發&#xff0c;定制化項目、 源碼、代碼講解、文檔撰寫、ppt制作。做自己喜歡的事&#xff0c;生活就是快樂的。 &#x1f34a;心愿&#xff1a;點…

【Linux系統】 4. 權限(一)

一. shell 命令及運行原理基本理解1&#xff09;廣義理解的操作系統包括&#xff1a;操作系統內核、外殼程序&#xff08;shell命令行、圖形化界面&#xff09;、必要的軟件。2&#xff09;狹義的操作系統&#xff1a;操作系統內核。3&#xff09;在用戶和內核之間有一個外殼程…

6.python——字符串

python中用’ 和" "創建字符串 python的子字符串截取用[]取字符串拼接可以直接用相加。 python三引號允許一個字符串跨多行&#xff0c;其中無需進行轉義&#xff08;所見即所得&#xff09;。 當你需要一塊HTML或者SQL時&#xff0c;這時用字符串組合&#xff0c;特…

足球數據API接口的技術特性與應用價值分析

一、接口概述現代足球數據接口是基于RESTful架構的數據服務&#xff0c;通過標準化方式提供賽事相關信息。這類接口通常采用JSON格式傳輸數據&#xff0c;支持跨平臺調用&#xff0c;為開發者提供結構化的足球賽事數據。二、數據覆蓋范圍主流足球數據接口通常包含以下數據類型&…