日誌是一個Web項目中必不可少的部分,藉助它咱們能夠作許多事情,好比問題排查、訪問統計、監控告警等。通常經過引入slf4j的一些實現框架來作日誌功能,如log4j,logback,log4j2,其性能也是依次加強。在springboot中,默認使用的框架是logback。咱們常常須要在方法開頭或結尾加日誌記錄傳入參數或返回結果,以此來複現當時的請求狀況。可是手動添加日誌,不只繁瑣重複,也影響代碼的美觀簡潔。本文引入一個基於AOP實現的日誌框架,並經過spring-boot-starter的方式完成集成。html
原文地址:http://blog.jboost.cn/2019/06/27/springboot-aoplog.html
java
項目地址: https://github.com/ronwxy/aop-logging
該項目基於 https://github.com/nickvl/aop-logging.git , 在其基礎上添加了ReqId來串聯某次客戶端請求(參考com.github.nickvl.xspring.core.log.aop.ReqIdFilter
), 添加了方法執行時長(參考com.github.nickvl.xspring.core.log.aop.AOPLogger.logTheMethod
方法中elapsedTime)。git
該項目提供了基於註解的AOP日誌功能。根據不一樣的日誌級別,提供的註解有LogTrace,LogDebug,LogInfo,LogWarn,LogError,LogFatal,LogException,可修飾於類(等同於該類內全部方法上添加)與方法上,前面六個分別表示在不一樣日誌級別下記錄方法被調用的日誌,LogException表示在方法拋出異常時,記錄相應日誌。這些註解都提供了一個LogPoint枚舉類型的屬性value,取值{IN,OUT,BOTH},分別表示在方法調用入口、方法調用返回前,以及包含二者的位置打印對應日誌,默認爲BOTH。github
能夠經過基於xml或基於java配置的方式來集成AOP日誌功能,我這裏基於java配置(基於xml的方式參考源碼README文件)而且經過spring-boot-starter的形式進行封裝(源碼地址: https://github.com/ronwxy/base-spring-boot ),避免每一個項目都須要配置。自動配置類以下spring
@Configuration @ConditionalOnClass(AOPLogger.class) @ConditionalOnMissingBean(AOPLogger.class) public class AopLoggerAutoConfiguration { private static final boolean SKIP_NULL_FIELDS = true; private static final Set<String> EXCLUDE_SECURE_FIELD_NAMES = Collections.emptySet(); @Bean public AOPLogger aopLogger() { AOPLogger aopLogger = new AOPLogger(); aopLogger.setLogAdapter(new UniversalLogAdapter(SKIP_NULL_FIELDS, EXCLUDE_SECURE_FIELD_NAMES)); return aopLogger; } /** * 註冊一個過濾器,用來生成一個reqId,標記一次請求,從而將本次請求所產生的日誌串聯起來 * @param * @return */ @Bean public FilterRegistrationBean reqIdFilter() { ReqIdFilter reqIdFilter = new ReqIdFilter(); FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(reqIdFilter); List<String> urlPatterns = Collections.singletonList("/*"); registrationBean.setUrlPatterns(urlPatterns); registrationBean.setOrder(100); return registrationBean; } }
將基礎框架base-spring-boot經過mvn clean install
進行本地安裝後,便可在項目中經過依賴進行引入(基礎框架中已在spring-boot-parent中引入,直接繼承亦可),如json
<dependency> <groupId>cn.jboost.springboot</groupId> <artifactId>aoplog-spring-boot-starter</artifactId> <version>1.2-SNAPSHOT</version> </dependency>
引入依賴以後,咱們再定義一個日誌配置文件logback-spring.xml,爲了後面方便地將日誌導入ELK作集中的日誌分析管理,該配置文件中將日誌以json格式輸出,並根據日誌級別分別寫入debug.log,info.log,warn.log,error.log以及interface.log(專用於接口訪問日誌),配置示例以下(完整配置參考: https://github.com/ronwxy/springboot-demos/blob/master/springboot-aoplog/src/main/resources/logback-spring.xml)api
<appender name="interfaceLog" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${logPath}/elk/interface.log</file> <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"> <providers> <pattern> <pattern> { "project": "${projectName}", "timestamp": "%date{\"yyyy-MM-dd'T'HH:mm:ss,SSSZ\"}", "log_level": "%level", "thread": "%thread", "class_name": "%X{callingClass}", "class_method":"%X{callingMethod}", "line_number": null, "message": "%message", "stack_trace": "%exception{5}", "req_id": "%X{reqId}", "elapsed_time": "#asLong{%X{elapsedTime}}" } </pattern> </pattern> </providers> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${logPath}/bak/interface.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> <totalSizeCap>1GB</totalSizeCap> </rollingPolicy> </appender>
爲了將該日誌配置文件能夠不經修改地達到複用,將一些參數配置外置了,故需在配置文件applicaiton.yml中配置以下參數 springboot
logger: path: D:\logs #默認當前項目路徑下的logs目錄 level: info # 默認info apiPackage: cn.jboost.springboot.aoplog.controller #必須配置, api接口類所在包 rootPackage: cn.jboost.springboot #必須配置,項目根包,記錄該包內各種經過slf4j輸出的日誌
最後,直接在須要記錄訪問日誌的接口類上加註解@LogInfo就好了,如 app
@RestController @RequestMapping("test") @LogInfo public class AoplogTestController { @GetMapping public String test(@RequestParam String user){ return "Hi " + user; } }
注意:在pom.xml中默認添加的spring-boot-maven-plugin下須要添加repackage的goal才能自動生成日誌目錄與日誌文件,以下所示 框架
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
啓動程序,調用@LogInfo標註的接口類下的API時,能夠看到控制檯有打印接口訪問日誌,如執行demo程序(源碼: https://github.com/ronwxy/springboot-demos/tree/master/springboot-aoplog ),調用 http://localhost:8080/test?user=jboost 時,控制檯打印日誌以下
[2019-06-27 14:29:59] [INFO ] [http-nio-8080-exec-1] [cn.jboost.springboot.aoplog.controller.AoplogTestController:184] --calling: test(user=jboost) [2019-06-27 14:29:59] [INFO ] [http-nio-8080-exec-1] [cn.jboost.springboot.aoplog.controller.AoplogTestController:189] --returning: test(1 arguments):Hi jboost
日誌文件interface.log中打印日誌以下,(其中req_id在本次請求的全部日誌都相同,這樣就能夠將一次請求的全部日誌串聯起來,便於分析與定位問題;elapsed_time標明瞭方法執行時長,可用於接口性能監測)
{"project":"aoplog-test","timestamp":"2019-06-27T14:29:59,030+0800","log_level":"INFO","thread":"http-nio-8080-exec-1","class_name":"cn.jboost.springboot.aoplog.controller.AoplogTestController","class_method":"test","line_number":null,"message":"calling: test(user=jboost)","stack_trace":"","req_id":"5d146267aa147904bc014e71","elapsed_time":null} {"project":"aoplog-test","timestamp":"2019-06-27T14:29:59,036+0800","log_level":"INFO","thread":"http-nio-8080-exec-1","class_name":"cn.jboost.springboot.aoplog.controller.AoplogTestController","class_method":"test","line_number":null,"message":"returning: test(1 arguments):Hi jboost","stack_trace":"","req_id":"5d146267aa147904bc014e71","elapsed_time":2}
Web項目中常常須要經過查看接口請求及返回參數來定位問題,手動編寫代碼打印顯得繁瑣而重複。使用aop-logging經過簡單的註解便可實現接口日誌自動打印。本文介紹的方案與日誌配置模板可直接用於實際項目開發。固然,註解不只可用於Controller層,也能夠用於Service等其它層,但通常Controller層加上便可,避免日誌打印過多。