SpringBoot 多线程中日志中增链路追踪trace id

代码地址:https://github.com/yangdaixai/traceId
在这里插入图片描述

log4j2 yml 配置

logging:
    config: src/main/resources/log4j2-spring.xml
    level:
        #    root: debug
        com:
            example: debug

log4j2-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5">
    <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->

    <!--变量配置-->
    <Properties>
        <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
        <!-- %logger{36} 表示 Logger 名字最长36个字符 -->
        <property name="LOG_PATTERN"  value="[TRACE_ID:%X{TRACE_ID}] %date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
        <!-- 定义日志存储的路径 -->
        <property name="FILE_PATH" value="E:/code/logs/demo_wwxx_log" />
        <property name="FILE_NAME" value="demo_wwxx_logy" />
    </Properties>

    <appenders>

        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </File>


        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileDebug" fileName="${FILE_PATH}/debug.log" filePattern="${FILE_PATH}/${FILE_NAME}-DEBUG-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>


        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

    </appenders>

    <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
    <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>

        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.mybatis" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
        <!--监控系统信息-->
        <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
        <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>

        <root level="debug">
            <appender-ref ref="Console"/>
            <appender-ref ref="Filelog"/>
            <appender-ref ref="RollingFileDebug"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>

</configuration>

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

@RestController
@RequestMapping("/test")
@Slf4j
public class ControllerTest {

    @GetMapping("/log4jtest")
    public String log4jtest() {

        MDC.put("TRACE_ID", UUID.randomUUID().toString());
        Map<String, String> context = MDC.getCopyOfContextMap();
        MDC.setContextMap(context);
        log.info("====new runable1 threadid:{}===",Thread.currentThread().getId());
        log.trace("trace level");
        log.debug("debug level");
        log.info("info level");
        log.warn("warn level");
        log.error("error level");
        return "success! log4jtest";
    }

}

在代码中直接添加

     	MDC.put("TRACE_ID", UUID.randomUUID().toString());
        Map<String, String> context = MDC.getCopyOfContextMap();
        new Thread(new Runnable() {
            @Override
            public void run() {
                MDC.setContextMap(context);
                log.info("====new runable1 threadid:{}===",Thread.currentThread().getId());

             //   Map<String, String> context = MDC.getCopyOfContextMap();
              String trceId =  MDC.get(TRACE_ID);

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //MDC.setContextMap(context);
                        MDC.put(TRACE_ID, trceId+"-runable2" );
                        log.info("====new runable2 threadid:{}====",Thread.currentThread().getId());
                    }
                }).start();
            }
        }).start();

在这里插入图片描述

也可以使用aop或者拦截器

aop

    private final static String TRACE_ID = "TRACE_ID";

    @Autowired
    HttpServletRequest request;
    
    public void setTraceId(){
        if (request != null && request.getHeader("Trace-Id") != null) {
            MDC.put(TRACE_ID, request.getHeader("Trace-Id"));
        }else{
            MDC.put("TRACE_ID", UUID.randomUUID().toString());
        }
    }

postman header中加入traceId相关参数
在这里插入图片描述

实现runable接口

import org.slf4j.MDC;
import java.util.Map;

/**
 * @author 
 * @description
 * @date 2022-06-24-4:36 PM
 */
public abstract class AbstractTracerRunnable implements Runnable{
    private Map<String, String> context = MDC.getCopyOfContextMap();

    public abstract void doTracerRun();

    @Override
    public void run() {

        if (this.context != null) {
            MDC.setContextMap(this.context);
        }

        try {
            this.doTracerRun();
        } finally {
            if (this.context != null) {
                MDC.clear();
            }
        }
    }
}

测试

        MDC.put("TRACE_ID", UUID.randomUUID().toString());

        new Thread(new AbstractTracerRunnable() {
            @Override
            public void doTracerRun() {
                log.info("====new AbstractTracerRunnable threadid:{}====",Thread.currentThread().getId());
            }
        }).start();

·在这里插入图片描述

实现callbale 接口

import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 * @author yangshengwei_zidie
 * @description
 * @date 2022-06-24-4:51 PM
 */
public abstract class AbstractTracerCallable<V> implements Callable<V> {

    private Map<String, String> context = MDC.getCopyOfContextMap();

public abstract V doTracer();

    @Override
    public V call() throws Exception {
        if (this.context != null) {
           MDC.setContextMap(this.context);
        }
        try{
            return doTracer();
        } finally {
            MDC.clear();
        }
    }
}

测试

        MDC.put("TRACE_ID", UUID.randomUUID().toString());

        FutureTask futureTask = new FutureTask<String>(new AbstractTracerCallable<String>(){

            @Override
            public String doTracer() {
                log.info("====new AbstractTracerCallable threadid:{}====",Thread.currentThread().getId());
                return null;
            }
        });
        Thread thread = new Thread(futureTask);
        thread.start();

在这里插入图片描述

线程池中使用


       MDC.put(TRACE_ID, UUID.randomUUID().toString());

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                5,
                5,
                1,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100),
                new ThreadFactoryBuilder().setNameFormat("test-thread-pool-%d").build());

        threadPoolExecutor.execute(new AbstractTracerRunnable(){
            @Override
            public void doTracerRun() {
                log.info("====new thread-pool-AbstractTracerRunnable threadid:{}====",Thread.currentThread().getId());
            }
        });

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值