logback打印日誌輸出線程ID:切面模式

一、前言

經常處理業務問題的同仁,一定都經常與日誌打交道。當併發量高、多線程編程時,日誌往往是一大堆,爲了快速精確的定位、處理問題,我們需要區分各個用戶的不同會話請求,需要從一坨坨日誌中做鏈路追蹤。

思路:在輸出日誌的時候,將每個線程的ID同時輸出,當然前提是保證每個線程的ID是唯一的。sl4j 提供的一個工具類MDC,支持 logback和log4j,作用就是擴展變量值到日誌中並輸出。

二、切面模式輸出線程ID

通過自定義切面,攔截有註解@LogId的請求,附加會話ID輸出到日誌。

  1. 加入POM引用
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
        <exclusions>
            <exclusion>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

     

  2. 自定義切點
    package cn.wuhg.climbing.service.design.patterns.sessionid.aspect;
    
    import java.lang.annotation.*;
    
    /**
     * 自定義日誌註解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface LogId {
        String value() default "";
    }
    

     

  3. 自定義日誌切面
    package cn.wuhg.climbing.service.design.patterns.sessionid.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.MDC;
    import org.springframework.stereotype.Component;
    
    import java.util.UUID;
    
    /**
     * 自定義日誌切面
     */
    @Aspect
    @Component
    public class LogAspect {
        private final static String SESSION_ID = "sessionId";
    
        /**
         * 自定義切點
         */
        @Pointcut("@annotation(cn.wuhg.climbing.service.design.patterns.sessionid.aspect.LogId)")
        public void pointCut() {
        }
    
        /**
         *  前置通知-記錄請求信息
         * @param joinPoint
         */
        @Before("pointCut()")
        public void doBeforeAdvice(JoinPoint joinPoint) {
            // MDC容器增加requestId
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            MDC.put(SESSION_ID, uuid);
    
        }
    
        /**
         * 後置通知-記錄返回信息
         * @param joinPoint
         * @param result
         */
        @AfterReturning(pointcut = "pointCut()", returning = "result")
        public void doAfterReturningAdvice(JoinPoint joinPoint, Object result) {
            MDC.remove(SESSION_ID);
        }
    
        /**
         * 後置異常通知-記錄返回出現異
         * @param joinPoint
         * @param exception
         */
        @AfterThrowing(value = "pointCut()", throwing = "exception")
        public void doAfterThrowingAdvice(JoinPoint joinPoint, Throwable exception) {
            MDC.remove(SESSION_ID);
        }
    
    }
    
    

     

  4. logback配置
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <contextName>SpringBootDemo</contextName>
        <property name="LOG_PATH" value="ToolLogs" />
        <!--設置系統日誌目錄 -->
        <property name="APPDIR" value="design-patterns" />
        <!-- 日誌記錄器,日期滾動記錄 -->
        <appender name="FILEALL"
    		class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/${APPDIR}/logs.log</file>
            <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!-- 歸檔的日誌文件的路徑,例如今天是2013-12-21日誌,當前寫的日誌文件路徑爲file節點指定,可以將此文件與file指定文件路徑設置爲不同路徑,從而將當前日誌文件或歸檔日誌文件置不同的目錄。 
    				而2013-12-21的日誌文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
                <fileNamePattern>${LOG_PATH}/${APPDIR}/logs-%d{yyyy-MM-dd}.%i.log
    			</fileNamePattern>
                <!-- 除按日誌記錄之外,還配置了日誌文件不能超過20M,若超過20M,日誌文件會以索引0開始, 命名日誌文件,例如log-error-2013-12-21.0.log -->
                <timeBasedFileNamingAndTriggeringPolicy
    				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>20MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <!-- 追加方式記錄日誌 -->
            <append>true</append>
            <!-- 日誌文件的格式 -->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{sessionId}] %-5level %class.%method Line:%-3L - %msg%n</pattern>
                <charset>utf-8</charset>
            </encoder>
        </appender>
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <!--encoder 默認配置爲PatternLayoutEncoder -->
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{sessionId}] %-5level %class.%method Line:%-3L - %msg%n</pattern>
                <charset>utf-8</charset>
            </encoder>
            <!--此日誌appender是爲開發使用,只配置最底級別,控制檯輸出的日誌級別是大於或等於此級別的日誌信息 -->
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>debug</level>
            </filter>
        </appender>
        <!-- 生產環境下,將此級別配置爲適合的級別,以免日誌文件太多或影響程序性能 -->
        <root level="INFO">
            <appender-ref ref="FILEALL" />
            <appender-ref ref="STDOUT" />
        </root>
    </configuration>

     

  5. Controller接口
    @LogId
    @GetMapping("/log")
        public void logId(){
        log.info("測試slf4j日誌線程ID");
    }
  6. 日誌截圖,紅框處是會話ID
  7. 整體切面過濾
    可以通過制定路徑範圍,來整體過濾,去除了@LogId註解的限制,僅需將自定義切面替換即可
    @Pointcut("execution(public * cn.wuhg.climbing.service.design.patterns..*(..))")
        public void pointCut() {
    }

三、往期推薦

logback打印日誌輸出線程ID:mvc攔截器模式

 

作者:Smile瀟灑Tel 

轉載請註明出處,謝謝合作!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章