本篇文章是接著 Spring boot 入門(四):整合 Shiro 實現登陸認證和許可權管理寫的,按照前面幾篇部落格的教程,可以搭建一個簡單的專案,主要包含了 Pagehelper+MyBatis 分頁查詢,Generator 程式碼自動生成器,Shiro登入及許可權管理。本篇部落格主要是整合 AOP 進行日誌管理
1.匯入 jar 包
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
複製程式碼
2.配置 Logback-spring 檔案
關於 Logback-spring 的配置網上很多,隨便copy一份基本上就能使用,Logback-spring.xml 中主要配置了下列內容
- (1).日誌寫道控制檯
- (2).日誌寫道本地檔案中
- (3).日誌級別
- (4).日誌生成方式(按照日期滾動生成,還是按照日期單獨生成)
- (5).日誌來源的配置,一般直接配置到 Control
我也是直接在copy了一份,程式碼如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<!--
<!– 檔案輸出格式 –>
<property name="PATTERN" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) |-%-5level [%thread] %c [%L] -| %msg%n" />
<!– test檔案路徑 –>
<property name="TEST_FILE_PATH" value="c:/log" />
-->
<!--輸出到控制檯-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--按天生成日誌-->
<appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Prudent>true</Prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>
applog/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log
</FileNamePattern>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{yyyy-MM-dd HH:mm:ss} -%msg%n
</Pattern>
</layout>
</appender>
<logger name="com.tswc.edu" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="logFile" />
</logger>
<root level="error">
<appender-ref ref="console"/>
<appender-ref ref="logFile" />
</root>
</configuration>
複製程式碼
這裡使用者也可以配置多個級別使用於多個環境,對每個日誌級別進行配置不同的屬性,然後在 Application.xml 中選擇不同的級別環境。在實際專案開發的過程中,一般配置2個環境,開發環境,生產環境。在開發環境中,只需要配置日誌輸出到控制檯,便於開發人員除錯。生成環境相反,需要配置日誌輸出到檔案,控制檯儘量不要輸出日誌,這樣可以減少控制檯對虛擬機器記憶體的消耗,一旦產生 Bug ,使用者查詢日誌檔案即可
上述程式碼中即配置了日誌輸出到控制檯,也配置了日誌輸出到日誌檔案
3.配置日誌級別
只需要在 Application.xml 中配置即可:
日誌級別分為5個等級,debug<info<warn<Error<Fatal,其中常用的級別為:debug和info- debug 級別最低,可以隨意的使用於任何覺得有利於在除錯時更詳細的瞭解系統執行狀態的東東;
- info 重要,輸出資訊:用來反饋系統的當前狀態給終端使用者的; 後三個,警告、錯誤、嚴重錯誤,這三者應該都在系統執行時檢測到了一個不正常的狀態。
- warn, 可修復,系統可繼續執行下去;
- Error, 可修復性,但無法確定系統會正常的工作下去;
- Fatal, 相當嚴重,可以肯定這種錯誤已經無法修復,並且如果系統繼續執行下去的話後果嚴重。
4.編寫日誌類
@Aspect
@Component
public class WebLogAspect {
private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
@Pointcut("execution( * com.tswc.edu.controller.*.*(..))")//兩個..代表所有子目錄,最後括號裡的兩個..代表所有引數
public void logPointCut() {
}
//切點
@Pointcut("@annotation(com.tswc.edu.annotation.Log)")
public void logPointCutLog() {
}
@Before("logPointCut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到請求,記錄請求內容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 記錄下請求內容
logger.info("請求地址 : " + request.getRequestURL().toString());
//logger.info("方法描述 : " + );
logger.info("HTTP METHOD : " + request.getMethod());
// 獲取真實的ip地址
//logger.info("IP : " + IPAddressUtil.getClientIpAddress(request));
logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
+ joinPoint.getSignature().getName());
logger.info("引數 : " + Arrays.toString(joinPoint.getArgs()));
// loggger.info("引數 : " + joinPoint.getArgs());
}
@AfterReturning(returning = "ret", pointcut = "logPointCut()")// returning的值和doAfterReturning的引數名一致
public void doAfterReturning(Object ret) throws Throwable {
// 處理完請求,返回內容(返回值太複雜時,列印的是物理儲存空間的地址)
logger.debug("返回值 : " + ret);
}
@Around("logPointCut()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
Object ob = pjp.proceed();// ob 為方法的返回值
logger.info("耗時 : " + (System.currentTimeMillis() - startTime));
return ob;
}
@Around("logPointCutLog()")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 執行方法
Object result = point.proceed();
// 獲得註解
Log controllerLog = getAnnotationLog(point);
if (controllerLog == null) {
return null;
}
String action = controllerLog.value();
logger.info("請求目的:"+action);
return result;
}
/**
* 是否存在註解,如果存在就獲取
*/
private static Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
//System.out.println("123:"+method.getAnnotation(Log.class).value());
return method.getAnnotation(Log.class);
}
//System.out.println("1234:"+method.getAnnotation(Log.class).value());
return null;
}
}
複製程式碼
這是個通用類,主要約定控制檯或者日誌檔案中日誌的格式,關於此公共類,網上有大量的講解,這裡就不詳細說明了。
再次啟動專案,控制檯將輸出日誌,並將日誌寫入到檔案中:
5.新增加部分
其中自定義日誌檔案可以不要,這裡使用者自己定義了日誌輸出的說明部分 自定義了 @Log 註記的識別,並配置一些檔案說明,那麼在請求到這個類的時候,日誌中將輸出文章描述部分自定義配置檔案的程式碼:
- Log
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
//自定義註解類 ArchivesLog.java(獲取Controller描述用的)
public @interface Log {
String value() default "";
}
複製程式碼
- LogAspect
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
//切點
@Pointcut("@annotation(com.tswc.edu.annotation.Log)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
// 執行方法
Object result = point.proceed();
// 執行時長(毫秒)
long time = System.currentTimeMillis() - beginTime;
//非同步儲存日誌
//saveLog(point, time);
return result;
}
}
複製程式碼
其中LogAspect中也可以寫一些對日誌進行 CRUD 的業務邏輯操作,大多數情況下,此處可以將日誌的儲存邏輯寫入到此類中。
6.問題
本專案在啟動的時候,報了一個關於日誌的警告,沒有找到解決方案
專案中並沒有用到log4j,不知道為什麼會警告,專案中缺少log4j的配置檔案,如果有大神知道原因,歡迎留言