logback 按照業務主鍵分文件打印日志,使用SiftingAppender結合MDC. 實現項目中定時任務的日志單獨打印,使用FilterReplay.NEUTRAL. 線程池和MDC


1. 需求: 按照業務主鍵划分日志分件,可以使用siftingAppender結合MDC 實現。 
<
appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"> <discriminator> <key>sysUUID</key> // 代碼使用使用MDC.put("sysUUID",value)即可 <defaultValue>unknown</defaultValue> </discriminator> <sift> <appender name="filexx" class="ch.qos.logback.core.FileAppender"> <Encoding>UTF-8</Encoding> <file>${log.base}/wtposp_${sysUUID}.log</file> // 按天生成文件夾為實現(%d{yyyyMMdd}這樣寫不行。) <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date{HH:mm:ss} [%X{sysUUID}] %-5level %c{0}:%L - %msg%n</pattern> </layout> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>TRACE</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> </appender> </sift> </appender>

 

 

2. 需求: 項目中有多個定時任務,每10s 執行一次,如果和其他業務日志打印到同一個文件,會造成日志文件很亂,業務日志中插着定時日志,

日志不連貫,不易拍錯,不易查看。於是想要把定時的相關日志打印到一個單獨的文件,其他日志另外打印。 

(1)最先想到的解決辦法是運用 logger 屬性 additivity=false ,可以讓日志不再往下傳遞。

  這種方式需要指定哪些包或文件 使用這個logger , 但是我項目的定時任務調用的方法和其他業務是同一個方法(比如查詢,我定時要用,其他業務也用啊),

  沒法區分開。 

 (2)使用一個標記,不如定時的日志內容都以 "JOB-" 開頭,我想辦法過濾下,使用appender 的 filter .  符合條件就打印,不符合就忽略或拒絕。

  需要定義兩個appender ,一個只接受JOB- 內容開頭的日志。 另一個不接受JOB- 內容開頭的日志。 

知識點:一個appender 可以包含多個filter ,常見的filter 是LevelFilter ,可以按日志級別過濾日志。  FilterReply的三個枚舉: 

   DENY: 放棄該日志,不再往下一個filter 傳遞

  ACCEPT:  處理該條日志,不再往下傳遞。

  NEUTRAL: 不處理,交給下一個filter 處理。 

<appender name="job-rollout" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Encoding>UTF-8</Encoding>
        <file>${log.base}/job_xx.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.base}/job-xx-%d{yyyy-MM-dd}.log.zip</fileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%date{HH:mm:ss} %-5level %c{0}:%L - %msg%n</pattern>
        </layout>
        <filter class="com.pp.filter.JobLogFilter">
            <onMatch>ACCEPT</onMatch>
            <onMismatch>NEUTRAL</onMismatch>
        </filter>
    </appender>

    <appender name="rollout" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Encoding>UTF-8</Encoding>   
        <file>${log.base}/xx.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <fileNamePattern>${log.base}/xx-%d{yyyy-MM-dd}.log.zip</fileNamePattern>
            <MaxHistory>30</MaxHistory> 
        </rollingPolicy>  
        <layout class="ch.qos.logback.classic.PatternLayout">  
            <pattern>%date{HH:mm:ss}   %-5level %c{0}:%L - %msg%n</pattern>
        </layout>
        <filter class="com.pp.filter.JobLogFilter">
            <onMatch>DENY</onMatch>
            <onMismatch>NEUTRAL</onMismatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">  
            <level>TRACE</level>  
            <onMatch>DENY</onMatch>  
            <onMismatch>ACCEPT</onMismatch>  
        </filter>
        <!-- <encoder>  
            <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{5} - %msg%n</pattern>  
        </encoder>  --> 
    </appender>  

<root>  
<level value="DEBUG" />
<appender-ref ref="job-rollout"/>
<appender-ref ref="rollout" />
</root>

 

/**
 * @Author zc
 * @Date 2019/8/16
* 繼承AbstractMatcherFilter是為了使用其屬下onMatch 和 onMisMatch 。 也可以直接繼承Filter,但是沒法傳遞參數進去,不能靈活區分是否打印日志。
*/ public class JobLogFilter extends AbstractMatcherFilter<ILoggingEvent> { @Override public FilterReply decide(ILoggingEvent event) { String uuid = event.getMDCPropertyMap().get("sysUUID"); if (uuid != null && uuid.contains("job")){ return this.onMatch; }else { return this.onMismatch; } } }

 

20190906 補充: 

結合MDC 知識,我使用MDC 存放業務主鍵,然后把業務主鍵在日志中打印,這樣我就能比較清晰的查看一個請求的所有日志了。

在請求入口

MDC.put("sysUUID",xx業務唯一);
然后
<layout class="ch.qos.logback.classic.PatternLayout">  
<pattern>%date{HH:mm:ss} [%X{sysUUID}] %-5level %c{0}:%L - %msg%n</pattern>
</layout>

打印出來的每行日志都有業務唯一標識。 

但是最近看日志發現一個問題: 日志中夾雜着多行sysUUID 不一樣的行,很是奇怪,排查發現都是使用線程池中的代碼打印的日志。

跟蹤代碼發現MDC中有ThreadLocal , 看來問題就是線程池使用的是單獨的線程,自然不一樣。 

解決辦法: 自定義線程,把主線程的上下文發給子線程,並保存到MDC.

Map _cm = MDC.getCopyOfContextMap();
pool.submit(new Runnable() {
@Override
public void run() {
MDC.setContextMap(_cm);
//業務代碼
//.....................
     //
MDC.clear();
}
});

封裝線程寫法樣例:
public abstract class A implments Runable{
private Map x ;
  public A(Map a){
    x = a;
}
  public abstract void cm();
  public void run(){
    
    MDC.setContextMap(x);
//業務代碼
//.....................
      cm();
     //
MDC.clear();

  }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM