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();
}
}