Spring Boot從入門到實戰:整合AOPLog來記錄介面訪問日誌

【空山新雨】發表於2019-06-27

日誌是一個Web專案中必不可少的部分,藉助它我們可以做許多事情,比如問題排查、訪問統計、監控告警等。一般通過引入slf4j的一些實現框架來做日誌功能,如log4j,logback,log4j2,其效能也是依次增強。在springboot中,預設使用的框架是logback。我們經常需要在方法開頭或結尾加日誌記錄傳入引數或返回結果,以此來複現當時的請求情況。但是手動新增日誌,不僅繁瑣重複,也影響程式碼的美觀簡潔。本文引入一個基於AOP實現的日誌框架,並通過spring-boot-starter的方式完成整合。

原文地址:http://blog.jboost.cn/2019/06/27/springboot-aoplog.html

1. aop-logging專案

專案地址: 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)。

該專案提供了基於註解的AOP日誌功能。根據不同的日誌級別,提供的註解有LogTrace,LogDebug,LogInfo,LogWarn,LogError,LogFatal,LogException,可修飾於類(等同於該類內所有方法上新增)與方法上,前面六個分別表示在不同日誌級別下記錄方法被呼叫的日誌,LogException表示在方法丟擲異常時,記錄相應日誌。這些註解都提供了一個LogPoint列舉型別的屬性value,取值{IN,OUT,BOTH},分別表示在方法呼叫入口、方法呼叫返回前,以及包含兩者的位置列印對應日誌,預設為BOTH。

2. 整合

可以通過基於xml或基於java配置的方式來整合AOP日誌功能,我這裡基於java配置(基於xml的方式參考原始碼README檔案)並且通過spring-boot-starter的形式進行封裝(原始碼地址: https://github.com/ronwxy/base-spring-boot ),避免每個專案都需要配置。自動配置類如下

@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中引入,直接繼承亦可),如

<dependency>
   <groupId>cn.jboost.springboot</groupId>
   <artifactId>aoplog-spring-boot-starter</artifactId>
   <version>1.2-SNAPSHOT</version>
</dependency>

 

3. 使用 

引入依賴之後,我們再定義一個日誌配置檔案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)

<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中配置如下引數 

logger:
  path: D:\logs #預設當前專案路徑下的logs目錄
  level: info # 預設info
  apiPackage: cn.jboost.springboot.aoplog.controller #必須配置, api介面類所在包
  rootPackage: cn.jboost.springboot #必須配置,專案根包,記錄該包內各類通過slf4j輸出的日誌

 

最後,直接在需要記錄訪問日誌的介面類上加註解@LogInfo就行了,如 

@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}

 

4. 總結 

Web專案中經常需要通過檢視介面請求及返回引數來定位問題,手動編寫程式碼列印顯得繁瑣而重複。使用aop-logging通過簡單的註解即可實現介面日誌自動列印。本文介紹的方案與日誌配置模板可直接用於實際專案開發。當然,註解不僅可用於Controller層,也可以用於Service等其它層,但一般Controller層加上即可,避免日誌列印過多。

本文示例專案原始碼地址:https://github.com/ronwxy/springboot-demos/tree/master/springboot-aoplog


我的個人部落格地址:http://blog.jboost.cn
我的github地址:https://github.com/ronwxy
我的微信公眾號:jboost-ksxy (歡迎關注,及時獲取技術乾貨分享)
——————————————————————————————————

微信公眾號
歡迎關注我的微信公眾號,及時獲取最新分享

相關文章