AOP
該切面僅用于請求日志記錄,若有其他需求,在此基礎上擴展即可,不多逼逼,直接上代碼。
引入切面依賴
<!-- 切面 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
日志切面類
import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;@Slf4j
@Aspect
@Component
public class RequestAop {private static final String START_TIME = "request-start";// 按需修改需要掃描的controller層@Pointcut("execution(* com.example.controller..*.*(..))")public void pointCut() {//該方法僅用于掃描controller包下類中的方法,而不做任何特殊的處理。}@Before("pointCut()")public void doBefore(JoinPoint joinPoint) {HttpServletRequest request =((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();Long start = System.currentTimeMillis();request.setAttribute(START_TIME, start);}@Around("pointCut()")@SneakyThrowspublic Object doAround(ProceedingJoinPoint joinPoint) {Object result = joinPoint.proceed();try {// 獲取方法名稱String method = joinPoint.getSignature().getName();// 獲取類名稱String className = joinPoint.getSignature().getDeclaringTypeName();// 獲取請求HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();// 請求路徑String requestUrl = request.getRequestURL().toString();// 獲取請求參數進行打印Signature signature = joinPoint.getSignature();// 參數名數組String[] parameterNames = ((MethodSignature) signature).getParameterNames();// 構造參數組集合List<Object> argList = new ArrayList<>();for (Object arg : joinPoint.getArgs()) {// request/response無法使用toJSONif (arg instanceof HttpServletRequest) {argList.add("request");} else if (arg instanceof HttpServletResponse) {argList.add("response");} else {argList.add(JSON.toJSON(arg));}}log.info("類名:[{}] 方法名:[{}] 請求URL:[{}] 請求參數:{} -> {} 請求結果:{}", className, method, requestUrl, JSON.toJSON(parameterNames), JSON.toJSON(argList), JSON.toJSON(result));} catch (Exception e) {log.error("切面類參數獲取失敗: {}", e.getMessage());}return result;}@After("pointCut()")public void doAfter(JoinPoint joinPoint) {HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();Long start = (Long) request.getAttribute(START_TIME);Long end = System.currentTimeMillis();// 耗時long costTime = end - start;// 方法名String method = joinPoint.getSignature().getName();log.info("方法名:[{}] 請求耗時:[{}ms]", method, costTime);}
}
日志
級別
日志級別(Log Levels)是指日志消息的優先級或者重要程度,它用于對日志的不同類型和重要程度進行分類和過濾。
不同的日志框架可能使用不同的命名和數量的日志級別,但基本概念是相似的。以下是常見的幾個標準日志級別:
1,TRACE(追蹤)
:最低級別的日志,包含詳細的調試信息,用于追蹤代碼的執行流程,如方法的輸入參數、內部狀態等。
2,DEBUG(調試)
:用于輸出調試信息,在開發和調試階段使用,幫助排查問題和跟蹤代碼執行情況以及驗證程序的行為。
3,INFO(信息)
:提供程序運行過程中的重要信息,用于向用戶提供一些關鍵的操作狀態和進度,如程序啟動關閉、配置項變更等。
4,WARN(警告)
:表示潛在的問題或異常情況,不會阻止程序繼續執行,但可能會影響程序的正常運行,需要開發人員注意。
5,ERROR(錯誤)
:表示錯誤情況,通常表示某個功能或步驟無法正常完成,但程序仍然可以繼續運行,需要開發人員關注和解決。
6,FATAL(致命)
:最高級別的日志,表示最嚴重的錯誤,表示程序無法繼續運行,會導致應用程序的中斷或崩潰,如系統崩潰。
特別說明:以上日志級別由上往下依次增強,而日志級別越高,控制臺打印出的日志信息就越少,但打印出的日志信息越重要。
引入lombok
依賴
引入lombok
后,在需要記錄日志的類上添加@Slf4j
注解即可。
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version><scope>provided</scope>
</dependency>
日志配置文件
在resources
下新建目錄logs
,logs
下新建logback-spring.xml
文件。
僅配置了常用的info
和error
級別,其余按需配置即可。
<?xml version="1.0" encoding="utf-8"?>
<configuration><!-- 引入默認得配置文件 --><include resource="org/springframework/boot/logging/logback/defaults.xml"/><!-- 模塊名標識日志名稱 --><springProperty scope="context" name="springAppName" source="spring.application.name"/><!-- info日志單文件大小限制 --><springProperty scope="context" name="logback.fileInfoLog.maxFileSize" source="logback.fileInfoLog.maxFileSize" defaultValue="1024MB" /><!-- info日志最大保留時長單位天 --><springProperty scope="context" name="logback.fileInfoLog.maxHistory" source="logback.fileInfoLog.maxHistory" defaultValue="30" /><!-- info日志文件總大小,超過該大小,舊得即將刪除 --><springProperty scope="context" name="logback.fileInfoLog.totalSizeCap" source="logback.fileInfoLog.totalSizeCap" defaultValue="10GB" /><!-- error日志單文件大小限制 --><springProperty scope="context" name="logback.fileErrorLog.maxFileSize" source="logback.fileErrorLog.maxFileSize" defaultValue="1024MB" /><!-- error日志最大保留時長單位天 --><springProperty scope="context" name="logback.fileErrorLog.maxHistory" source="logback.fileErrorLog.maxHistory" defaultValue="30" /><!-- error日志文件總大小,超過該大小,舊得即將刪除 --><springProperty scope="context" name="logback.fileErrorLog.totalSizeCap" source="logback.fileErrorLog.totalSizeCap" defaultValue="10GB" /><!-- 日志目錄 --><springProperty scope="context" name="logback.rootDir" source="logback.rootDir" defaultValue="logs"/><!-- 控制臺輸出得日志格式 --><property name="CONSOLE_LOG_PATTERN"value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/><!-- 日志文件輸出得日志格式 --><property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %t [%c:%L]-%m%n"/><!-- 控制臺輸出 --><appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender"><layout class="ch.qos.logback.classic.PatternLayout"><pattern>${CONSOLE_LOG_PATTERN}</pattern></layout></appender><!-- info日志得設定 --><appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>DENY</onMatch><onMismatch>ACCEPT</onMismatch></filter><encoder><pattern>${FILE_LOG_PATTERN}</pattern></encoder><file>${logback.rootDir}/${springAppName}.log</file><!--滾動策略--><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy" ><!--路徑--><fileNamePattern>${logback.rootDir}/%d{yyyy-MM,aux}/%d{yyyy-MM-dd,aux}/${springAppName}-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern><maxFileSize>${logback.fileInfoLog.maxFileSize}</maxFileSize><maxHistory>${logback.fileInfoLog.maxHistory}</maxHistory><totalSizeCap>${logback.fileInfoLog.totalSizeCap}</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy></appender><!-- 錯誤日志 --><appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>ERROR</level></filter><encoder><pattern>${FILE_LOG_PATTERN}</pattern></encoder><file>${logback.rootDir}/${springAppName}-error.log</file><!--滾動策略--><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy" ><!--路徑--><fileNamePattern>${logback.rootDir}/%d{yyyy-MM,aux}/%d{yyyy-MM-dd,aux}/${springAppName}-error-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern><maxFileSize>${logback.fileErrorLog.maxFileSize}</maxFileSize><maxHistory>${logback.fileErrorLog.maxHistory}</maxHistory><totalSizeCap>${logback.fileErrorLog.totalSizeCap}</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy></appender><appender name="ASYNC_consoleLog" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="consoleLog"/></appender><appender name="ASYNC_fileInfoLog" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="fileInfoLog"/></appender><appender name="ASYNC_fileErrorLog" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="fileErrorLog"/></appender><root level="info"><appender-ref ref="ASYNC_consoleLog" /><appender-ref ref="ASYNC_fileInfoLog" /><appender-ref ref="ASYNC_fileErrorLog" /></root></configuration>
application.yml
配置
# spring.application.name 必須配置
# 因為上述日志配置文件指定了項目啟動后輸出的日志文件命名,即為該配置
spring:application:name: LogApplicationlogging:
# 指定自定義的配置文件config: classpath:logs/logback-spring.xml
# 指定輸出的日志級別
# trace < debug < info < warn < error
# 例如:指定輸出級別為info,則trace和debug均不會輸出level:root: info #該方式指定的是整個項目的日志輸出級別# com.example.controller: debug #也可以指定具體某個包下的日志輸出級別
結果展示
以上述配置為例,項目啟動后會在項目下生成logs
目錄,該目錄下會有兩個日志文件:LogApplication.log
和 LogApplication-error.log
,項目中所有log.error()
日志都會輸出到LogApplication-error.log
,其余日志則輸出到LogApplication.log
.
拓展
將指定的類產生的日志輸出到指定的文件中。
示例:RequestAop
切面中產生的是所有的請求記錄,將該類的日志放入指定的文件。
logback-spring.xml
新增配置,未添加請求日志文件的大小限制、存放時間等配置,若有需求,按info
、error
配置仿寫即可。
<!-- 接口請求日志 --><appender name="requestLog" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>DENY</onMatch><onMismatch>ACCEPT</onMismatch></filter><encoder><pattern>${FILE_LOG_PATTERN}</pattern></encoder><!--此處配置輸出文件名稱為 應用名-request.log --><file>${logback.rootDir}/${springAppName}-request.log</file><!--滾動策略--><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy" ><!--路徑--><fileNamePattern>${logback.rootDir}/%d{yyyy-MM,aux}/%d{yyyy-MM-dd,aux}/${springAppName}-request-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern><maxFileSize>${logback.fileInfoLog.maxFileSize}</maxFileSize><maxHistory>${logback.fileInfoLog.maxHistory}</maxHistory><totalSizeCap>${logback.fileInfoLog.totalSizeCap}</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy></appender><appender name="ASYNC_requestLog" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="requestLog"/></appender><!--將切面類所在包的位置配置上--><logger name="com.example.aop.RequestAop" additivity="false" level="INFO"><appender-ref ref="ASYNC_requestLog"/></logger>
以上述配置為例,項目啟動后會在項目下生成logs
目錄,該目錄下會有三個日志文件:LogApplication.log
、 LogApplication-error.log
、LogApplication-request.log
,項目中所有log.error()
日志都會輸出到LogApplication-error.log
,RequestAop
切面類的日志會輸出到LogApplication-request.log
,其余日志則輸出到LogApplication.log
.