1. 作用
Logback将写日志事件的任务委托给appender组件完成,SiftingAppender顾名思义就是筛选日志事件,具体点就是:
对于Logback委托给它的日志事件,SiftingAppender会对日志事件做一些区分,然后不同的事件SiftingAppender会委托不同的appender去完成真正的写操作。
假设有这么一个场景:
有一个Task类完成成一些特定的任务,每一个Task实例都有一个编号,运行时期会不停的输出任务状态的日志。现在我们需要每个任务按照编号把日志输出到一个特定的日志文件中。
接下来,借助 slf4j 的 SiftingAppender。一起完成这样一个场景。
2. 使用SiftingAppender
slf4j的配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true">
<property name="pattern" value="%d{yyyyMMdd:HH:mm:ss.SSS} [%thread] %-5level %msg%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<!--discriminator鉴别器,根据taskId这个key对应的value鉴别日志事件,然后委托给具体appender写日志-->
<discriminator>
<key>taskId</key>
<defaultValue>default</defaultValue>
</discriminator>
<sift>
<!--具体的写日志appender,每一个taskId创建一个文件-->
<appender name="File-${taskId}" class="ch.qos.logback.core.FileAppender">
<file>d:/log/${taskId}/info.log</file>
<append>true</append>
<encoder charset="UTF-8">
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
</sift>
</appender>
<!--应用于该包路径的日志-->
<logger name="com.github.thinwonton.showcase.java.slf4j.task" level="INFO">
<appender-ref ref="SIFT"/>
</logger>
<root level="INFO">
<level>info</level>
<appender-ref ref="STDOUT"/>
</root>
</configuration>
有个task类,在里面完成一些特定的业务功能。
package com.github.thinwonton.showcase.java.slf4j.task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
// 任务类
public class MyTask implements Runnable {
private static Logger LOG = LoggerFactory.getLogger(MyTask.class);
private String taskId;
public MyTask(String taskId) {
this.taskId = taskId;
}
public void run() {
try {
/* 上面logback.xml中discriminator根据taskId这个key的value来决定,taskId的value通过这种方式设置,
这里设置的key-value对是保存在一个ThreadLocal<Map>中的,所以不会对其他线程中的taskId这个key产生影响
*/
MDC.put("taskId", taskId);
for (; ; ) {
// 写日志,使用SiftingAppender,由于当前调用线程taskId的value是对应this.taskId(假设是task-0),
// 所以会输出到task-0这个文件中
LOG.info("taskId={}, threadNo={}", taskId, Thread.currentThread());
Thread.sleep(2000);
}
} catch (Exception e) {
} finally {
MDC.remove(taskId);
}
}
}
MDC.put("taskId", taskId); 是关键
执行类:
public class SiftingAppenderTest {
public static void main(String[] args) {
ExecutorService taskExecutors = Executors.newCachedThreadPool();
// 运行10个task,启动了10个线程
for (int i = 0; i < 10; ++i) {
taskExecutors.submit(new MyTask("task-" + i));
}
taskExecutors.shutdown();
}
}
运行程序后,task的日志将会按照它的ID写入到不同的文件夹中。