Spring Boot2集成AOPLog來(lái)記錄接口訪問(wèn)日志
前言
日志是一個(gè)Web項(xiàng)目中必不可少的部分,借助它我們可以做許多事情,比如問(wèn)題排查、訪問(wèn)統(tǒng)計(jì)、監(jiān)控告警等。一般通過(guò)引入slf4j的一些實(shí)現(xiàn)框架來(lái)做日志功能,如log4j,logback,log4j2,其性能也是依次增強(qiáng)。在springboot中,默認(rèn)使用的框架是logback。
我們經(jīng)常需要在方法開(kāi)頭或結(jié)尾加日志記錄傳入?yún)?shù)或返回結(jié)果,以此來(lái)復(fù)現(xiàn)當(dāng)時(shí)的請(qǐng)求情況。但是手動(dòng)添加日志,不僅繁瑣重復(fù),也影響代碼的美觀簡(jiǎn)潔。本文引入一個(gè)基于AOP實(shí)現(xiàn)的日志框架,并通過(guò)spring-boot-starter的方式完成集成。
1. aop-logging項(xiàng)目
項(xiàng)目地址
該項(xiàng)目基于 , 在其基礎(chǔ)上添加了ReqId來(lái)串聯(lián)某次客戶端請(qǐng)求(參考com.github.nickvl.xspring.core.log.aop.ReqIdFilter), 添加了方法執(zhí)行時(shí)長(zhǎng)(參考com.github.nickvl.xspring.core.log.aop.AOPLogger.logTheMethod方法中elapsedTime)。
該項(xiàng)目提供了基于注解的AOP日志功能。根據(jù)不同的日志級(jí)別,提供的注解有LogTrace,LogDebug,LogInfo,LogWarn,LogError,LogFatal,LogException,可修飾于類(等同于該類內(nèi)所有方法上添加)與方法上,前面六個(gè)分別表示在不同日志級(jí)別下記錄方法被調(diào)用的日志,LogException表示在方法拋出異常時(shí),記錄相應(yīng)日志。
這些注解都提供了一個(gè)LogPoint枚舉類型的屬性value,取值{IN,OUT,BOTH},分別表示在方法調(diào)用入口、方法調(diào)用返回前,以及包含兩者的位置打印對(duì)應(yīng)日志,默認(rèn)為BOTH。
2. 集成
可可以通過(guò)基于xml或基于java配置的方式來(lái)集成AOP日志功能,我這里基于java配置(基于xml的方式參考源碼README文件)并且通過(guò)spring-boot-starter的形式進(jìn)行封裝(源碼地址),避免每個(gè)項(xiàng)目都需要配置。自動(dòng)配置類如下
@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; } /** * 注冊(cè)一個(gè)過(guò)濾器,用來(lái)生成一個(gè)reqId,標(biāo)記一次請(qǐng)求,從而將本次請(qǐng)求所產(chǎn)生的日志串聯(lián)起來(lái) * @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; } }
將基礎(chǔ)框架base-spring-boot通過(guò)mvn clean install進(jìn)行本地安裝后,即可在項(xiàng)目中通過(guò)依賴進(jìn)行引入(基礎(chǔ)框架中已在spring-boot-parent中引入,直接繼承亦可),如
<dependency> <groupId>cn.jboost.springboot</groupId> <artifactId>aoplog-spring-boot-starter</artifactId> <version>1.2-SNAPSHOT</version> </dependency>
3. 使用
引入依賴之后,我們?cè)俣x一個(gè)日志配置文件logback-spring.xml,為了后面方便地將日志導(dǎo)入ELK做集中的日志分析管理,該配置文件中將日志以json格式輸出,并根據(jù)日志級(jí)別分別寫(xiě)入debug.log,info.log,warn.log,error.log以及interface.log(專用于接口訪問(wèn)日志),配置示例如下(完整配置)
<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>
為了將該日志配置文件可以不經(jīng)修改地達(dá)到復(fù)用,將一些參數(shù)配置外置了,故需在配置文件applicaiton.yml中配置如下參數(shù)
logger: path: D:\logs #默認(rèn)當(dāng)前項(xiàng)目路徑下的logs目錄 level: info # 默認(rèn)info apiPackage: cn.jboost.springboot.aoplog.controller #必須配置, api接口類所在包 rootPackage: cn.jboost.springboot #必須配置,項(xiàng)目根包,記錄該包內(nèi)各類通過(guò)slf4j輸出的日志
最后,直接在需要記錄訪問(wèn)日志的接口類上加注解@LogInfo就行了,如
@RestController @RequestMapping("test") @LogInfo public class AoplogTestController { @GetMapping public String test(@RequestParam String user){ return "Hi " + user; } }
注意:在pom.xml中默認(rèn)添加的spring-boot-maven-plugin下需要添加repackage的goal才能自動(dòng)生成日志目錄與日志文件,如下所示
<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>
啟動(dòng)程序,調(diào)用@LogInfo標(biāo)注的接口類下的API時(shí),可以看到控制臺(tái)有打印接口訪問(wèn)日志,如執(zhí)行demo程序(源碼),調(diào)用 http://localhost:8080/test?user=jboost 時(shí),控制臺(tái)打印日志如下
[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在本次請(qǐng)求的所有日志都相同,這樣就可以將一次請(qǐng)求的所有日志串聯(lián)起來(lái),便于分析與定位問(wèn)題;elapsed_time標(biāo)明了方法執(zhí)行時(shí)長(zhǎng),可用于接口性能監(jiān)測(cè))
{"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}
4. 總結(jié)
Web項(xiàng)目中經(jīng)常需要通過(guò)查看接口請(qǐng)求及返回參數(shù)來(lái)定位問(wèn)題,手動(dòng)編寫(xiě)代碼打印顯得繁瑣而重復(fù)。使用aop-logging通過(guò)簡(jiǎn)單的注解即可實(shí)現(xiàn)接口日志自動(dòng)打印。本文介紹的方案與日志配置模板可直接用于實(shí)際項(xiàng)目開(kāi)發(fā)。當(dāng)然,注解不僅可用于Controller層,也可以用于Service等其它層,但一般Controller層加上即可,避免日志打印過(guò)多。
本文示例項(xiàng)目源碼地址
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中Set&List的迭代器實(shí)現(xiàn)步驟解析
這篇文章主要介紹了Java中Set&List的迭代器實(shí)現(xiàn)步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Java?對(duì)象深拷貝工具類的實(shí)現(xiàn)
本文主要介紹了Java?對(duì)象深拷貝工具類的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07java ThreadPoolExecutor使用方法簡(jiǎn)單介紹
這篇文章主要介紹了java ThreadPoolExecutor使用方法簡(jiǎn)單介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02Java中的數(shù)組流ByteArrayOutputStream用法
Java中的ByteArrayOutputStream是java.io包中的一個(gè)類,用于在內(nèi)存中創(chuàng)建字節(jié)數(shù)組緩沖區(qū),支持動(dòng)態(tài)擴(kuò)展,它繼承自O(shè)utputStream,允許以字節(jié)形式寫(xiě)入數(shù)據(jù),無(wú)需與外部設(shè)備交互,常用方法包括write()、toByteArray()、toString()等2024-09-09SpringBoot整合MongoDB完整實(shí)例代碼
本文主要介紹了SpringBoot整合MongoDB完整實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02Java org.w3c.dom.Document 類方法引用報(bào)錯(cuò)
這篇文章主要介紹了Java org.w3c.dom.Document 類方法引用報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08通過(guò)Feign進(jìn)行調(diào)用@FeignClient?找不到的解決方案
這篇文章主要介紹了通過(guò)Feign進(jìn)行調(diào)用@FeignClient?找不到的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03java雙端隊(duì)列之ArrayDequeue原理講解
這篇文章主要為大家介紹了java雙端隊(duì)列之ArrayDequeue原理講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06