Spring Boot使用AOP搭建統一處理請求日誌和使用log4j記錄不同級別的日誌
受http://blog.didispace.com/springbootaoplog/啟發,今天給Spring Boot專案搭建了統一處理請求日誌的切面並引入log4j記錄不同層級日誌。 mark一下這個過程,以及原文中沒有涉及到的一些疑問
一. 新增要使用的依賴
<!--日誌--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!--aop--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.0.4.RELEASE</version> </dependency>
在新匯入依賴的過程中剛好遇到公司網路從maven中央倉庫下載依賴受阻,所以也切換了專案的依賴源,在此順便記錄一下
修改apache-maven-3.3.9中的conf資料夾下的setting.xml
檔案內容,在<mirrors>節點下新增
<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror>
再將IDEA中maven配置的“User settings file”修改成setting.xml
檔案所在路徑(預設路徑/Users/xxx/.m2/setting.xml)
二.
實現Web層的日誌切面,關於這個類的相關疑問可以參考頂部連結的文章
import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; 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 java.util.Arrays; @Aspect @Order(0) @Component public class WebLogAspect { ThreadLocal<Long> startTime = new ThreadLocal<>(); private Logger logger = Logger.getLogger(getClass()); @Pointcut("execution(public * com.example.controller..*.*(..))") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) { startTime.set(System.currentTimeMillis()); // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); if (attributes != null) { HttpServletRequest request = attributes.getRequest(); // 記錄下請求內容 System.out.println("\r\n"); logger.info("地址 : " + request.getRequestURL().toString()); logger.info("請求方式 : " + request.getMethod()); logger.info("IP : " + request.getRemoteAddr()); logger.info("執行的方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); Object[] args = joinPoint.getArgs().clone(); logger.info("引數 : " + Arrays.toString(args)); } } /** * 處理完請求,返回內容 * @param ret */ @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) { logger.info("返回內容 : " + ret); logger.info("花費時間 : " + (System.currentTimeMillis() - startTime.get()) + "毫秒"); } }
通過@Pointcut
定義切入點,此處是com.example.controller
包下的所有Controller(對controller層所有請求處理做切入點),然後通過@Before
實現對請求內容的日誌記錄,最後通過@AfterReturning
記錄請求返回的物件。
實現AOP的切面主要有以下幾個要素:(轉)
- 使用
@Aspect
註解將一個java類定義為切面類 - 使用
@Pointcut
定義一個切入點,可以是一個規則表示式,比如下例中某個package下的所有函式,也可以是一個註解等。 - 根據需要在切入點不同位置的切入內容
- 使用
@Before
在切入點開始處切入內容 - 使用
@After
在切入點結尾處切入內容 - 使用
@AfterReturning
在切入點return內容之後切入內容(可以用來對處理返回值做一些加工處理) - 使用
@Around
在切入點前後切入內容,並自己控制何時執行切入點自身的內容 - 使用
@AfterThrowing
用來處理當切入內容部分丟擲異常之後的處理邏輯
- 使用
三. 使用log4j.properties記錄更詳細的日誌
在resources資料夾下與application.yml檔案同級目錄 建立log4j.properties,這樣將在專案根目錄下建立一個logs的目錄,用於存放不同的日誌檔案。
andlers= java.util.logging.ConsoleHandler
redirect.commons.logging = enabled
log4j.rootLogger=DEBUG, STDOUT, FILE, DAILY_FILE, ROLLING_FILE
### 輸出DEBUG 級別以上的日誌 ###
log4j.appender.DEBUG = org.apache.log4j.DailyRollingFileAppender
log4j.appender.DEBUG.File = logs/debug.log
log4j.appender.DEBUG.Append = true
log4j.appender.DEBUG.Threshold = DEBUG
log4j.appender.DEBUG.layout = org.apache.log4j.PatternLayout
log4j.appender.DEBUG.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### direct log messages to stdout ###
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.Target=System.out
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - %c{1}:%L -%-4r - %-5p - %m%n
### direct log messages to file ###
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=logs/file.log
###log4j.appender.FILE.File=${webapp.root}/WEB-INF/logs/file.log ###
### log4j:ERROR setFile(null,true) call failed.java.io.FileNotFoundException ###
log4j.appender.FILE.Append=true
log4j.appender.FILE.Threshold=ERROR
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### direct log messages to daily file ###
log4j.appender.DAILY_FILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DAILY_FILE.File=logs/daily.log
log4j.appender.DAILY_FILE.Encoding=UTF-8
log4j.appender.DAILY_FILE.Threshold=INFO
log4j.appender.DAILY_FILE.DatePattern='.'yyyy-MM-dd
log4j.appender.DAILY_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.DAILY_FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L : %m%n
### direct log messages to rolling file ###
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=ERROR
log4j.appender.ROLLING_FILE.File=logs/rolling.log
log4j.appender.ROLLING_FILE.Append=true
log4j.appender.CONSOLE_FILE.Encoding=UTF-8
log4j.appender.ROLLING_FILE.MaxFileSize=1000KB
log4j.appender.ROLLING_FILE.MaxBackupIndex=1
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %-d{yyyy-MM-dd HH:mm:ss} - %c -%-4r [%t] %-5p %c %x - %m%n
### direct log messages to socket ###
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost=localhost
log4j.appender.SOCKET.Port=5001
log4j.appender.SOCKET.LocationInfo=true
# Set up for Log Facter 5
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n
# Log Factor 5 Appender
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
四. 搭建過程中遇到的一些疑問
1. 報錯:log4j:WARN No appenders could be found for logger (com.example.aspect.WebLogAspect).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
由於log4j.properties檔案配置不夠完整,使用上述的檔案就可以了
2. Log4J配置後,啟動專案時控制檯出現 log4j:ERROR setFile(null,true) call failed. 報錯:java.io.FileNotFoundException
由於之前log4j.properties檔案中配置的是log4j.appender.FILE.File=${webapp.root}/WEB-INF/logs/file.log。tomcat中也有這個同名的檔案,tomcat啟動是預設去找log4j.properties,但此時Listener還沒有起來,tomcat就要往/WEB-INF/logs/log4j.log 寫日誌就找不到了。 解決: 將路徑改成 logs/xxx.log, 這樣就將這些不同日誌檔案生成在專案根目錄下的logs目錄中。
3. 關於Spring Boot專案中使用AOP是否需要在yml檔案中配置
根據查閱各位大佬的文章發現,Spring
Boot
對AOP
的預設配置屬性是開啟的,也就是說spring.aop.auto
屬性的值預設是true;
同時我們只要引入了AOP
依賴後,預設就已經增加了@EnableAspectJAutoProxy
功能,不需要我們在程式啟動類上面加入註解@EnableAspectJAutoProxy,也不需要在yml中配置spring.aop.proxy-target-class,不過這個預設值是false即使用的是JDK動態代理,
當我們需要使用CGLIB來實現AOP的時候,需要配置spring.aop.proxy-target-class=true。