1、Slf4j MDC线程安全测试

类似ThreadLocal使用

@Slf4j
public class Main {

    public static void main(String[] args) {

        for (int i = 1 ; i <= 10 ; i++) {
            Thread thread = new Thread(new WorkerThread("Thread " + i));
            thread.start();
        }
    }

    @Slf4j
    static class WorkerThread implements Runnable {

        private String threadName;

        public WorkerThread(String threadName) {
            this.threadName = threadName;
        }

        @Override
        public void run() {
            log.info("input : {}", Thread.currentThread().getName() + "->>" + threadName);
            MDC.put("threadName", Thread.currentThread().getName() + "->>" + threadName);


            // 模拟一些操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                log.error("error.", e);
            }

            log.info("output : {}", MDC.get("threadName"));

        }
    }
}

输出结果 :

[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-3->>Thread 4
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-9->>Thread 10
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-6->>Thread 7
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-0->>Thread 1
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-7->>Thread 8
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-4->>Thread 5
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-2->>Thread 3
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-5->>Thread 6
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-1->>Thread 2
[INFO] 2024-06-17 11:28:07.066 +0800 c.j.s.s.Main$WorkerThread:[31] - input : Thread-8->>Thread 9
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-3->>Thread 4
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-5->>Thread 6
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-0->>Thread 1
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-8->>Thread 9
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-1->>Thread 2
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-4->>Thread 5
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-7->>Thread 8
[INFO] 2024-06-17 11:28:08.070 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-2->>Thread 3
[INFO] 2024-06-17 11:28:08.071 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-9->>Thread 10
[INFO] 2024-06-17 11:28:08.073 +0800 c.j.s.s.Main$WorkerThread:[42] - output : Thread-6->>Thread 7

可以当做ThreadLocal来使用,是线程安全的

2、模拟 dolphinscheduler Task日志打印

2.1、pom.xml依赖

<dependencies>
    <!-- SLF4J API -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.32</version>
    </dependency>

    <!-- SLF4J Implementation (e.g., Logback) -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.6</version>
    </dependency>
</dependencies>

2.2、TaskLogFilter

@Slf4j
public class TaskLogFilter extends Filter<ILoggingEvent> {

    @Override
    public FilterReply decide(ILoggingEvent event) {
        return MDC.get("taskInstanceLogFullPath") == null ? FilterReply.DENY : FilterReply.ACCEPT;
    }
}

2.3、TaskLogDiscriminator

@Slf4j
public class TaskLogDiscriminator extends AbstractDiscriminator<ILoggingEvent> {

    private String key;

    private String logBase;

    @Override
    public String getDiscriminatingValue(ILoggingEvent event) {
        String taskInstanceLogPath = MDC.get("taskInstanceLogFullPath");
        if (taskInstanceLogPath == null) {
            log.error("The task instance log path is null, please check the logback configuration, log: {}", event);
        }
        return taskInstanceLogPath;
    }

    @Override
    public void start() {
        started = true;
    }

    @Override
    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getLogBase() {
        return logBase;
    }

    public void setLogBase(String logBase) {
        this.logBase = logBase;
    }
}

2.4、logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="120 seconds">
    <property name="log.base" value="logs"/>
    <property scope="context" name="log.base.ctx" value="${log.base}" />

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                [%level] %date{yyyy-MM-dd HH:mm:ss.SSS Z} %logger{10}:[%line] - %msg%n
            </pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <appender name="TASKLOGFILE" class="ch.qos.logback.classic.sift.SiftingAppender">
        <filter class="com.journey.springboot.log.TaskLogFilter"/>
        <Discriminator class="com.journey.springboot.log.TaskLogDiscriminator">
            <key>taskInstanceLogFullPath</key>
            <logBase>${log.base}</logBase>
        </Discriminator>
        <sift>
            <appender name="FILE-${taskInstanceLogFullPath}" class="ch.qos.logback.core.FileAppender">
                <file>${taskInstanceLogFullPath}</file>
                <encoder>
                    <pattern>
                        [%level] %date{yyyy-MM-dd HH:mm:ss.SSS Z} - %message%n
                    </pattern>
                    <charset>UTF-8</charset>
                </encoder>
                <append>true</append>
            </appender>
        </sift>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="TASKLOGFILE"/>
    </root>
</configuration>

2.5、示例

@Slf4j
public class Main {

    public static void main(String[] args) {

        String taskFirstSubmitTime = "20140617";
        Long workflowDefinitionCode = 11123333L;
        int workflowDefinitionVersion = 1;
        int workflowInstanceId = 1234;
        int taskInstanceId = 345;

        String  taskInstanceLogFullPath = getTaskInstanceLogFullPath(
                taskFirstSubmitTime,
                workflowDefinitionCode,
                workflowDefinitionVersion,
                workflowInstanceId,
                taskInstanceId);

        MDC.put("taskInstanceLogFullPath", taskInstanceLogFullPath);

        log.info("task start.");
        log.info("task execute.");
        log.info("task end.");

    }

    public static Path getTaskInstanceLogBasePath() {
        return Optional.of(LoggerFactory.getILoggerFactory())
                .map(e -> (AppenderAttachable<ILoggingEvent>) (e.getLogger("ROOT")))
                .map(e -> (SiftingAppender) (e.getAppender("TASKLOGFILE")))
                .map(e -> ((TaskLogDiscriminator) (e.getDiscriminator())))
                .map(TaskLogDiscriminator::getLogBase)
                .map(e -> Paths.get(e).toAbsolutePath())
                .orElse(null);
    }

    public static String getTaskInstanceLogFullPath(String taskFirstSubmitTime,
                                                    Long workflowDefinitionCode,
                                                    int workflowDefinitionVersion,
                                                    int workflowInstanceId,
                                                    int taskInstanceId) {
        final String taskLogFileName = Paths.get(
                String.valueOf(workflowDefinitionCode),
                String.valueOf(workflowDefinitionVersion),
                String.valueOf(workflowInstanceId),
                String.format("%s.log", taskInstanceId)).toString();
        return getTaskInstanceLogBasePath()
                .resolve(taskFirstSubmitTime)
                .resolve(taskLogFileName)
                .toString();
    }
}

2.6、结果

image.png

如感兴趣,点赞加关注,谢谢!!!


journey
32 声望20 粉丝

引用和评论

0 条评论