千家信息网

Springboot+MDC+traceId日志中如何打印唯一traceId

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章主要为大家展示了"Springboot+MDC+traceId日志中如何打印唯一traceId",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Spr
千家信息网最后更新 2026年10月31日Springboot+MDC+traceId日志中如何打印唯一traceId

这篇文章主要为大家展示了"Springboot+MDC+traceId日志中如何打印唯一traceId",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Springboot+MDC+traceId日志中如何打印唯一traceId"这篇文章吧。

    先看一张图:

    有同学问:日志中[]中类似uuid的这个traceId是怎么实现的,这边文章就介绍下如何在springboot工程下用MDC实现日志文件中打印traceId。

    1. 为什么需要这个traceId

    我们在定位问题的时候需要去日志中查找对应的位置,当我们一个接口的请求同用唯一的一个traceId,那我们只需要知道这个traceId,使用 grep 'traceId' xxx.log 语句就能准确的定位到目标日志。在这边文章会介绍如何去设置这个traceId,而后如何在接口返回这个traceId。

    #接口返回:{    "code": "0",    "dealStatus": "1",    "TRACE_ID": "a10e6e8d-9953-4de9-b145-32eee6aa5562"}#查询日志 grep 'a10e6e8d-9953-4de9-b145-32eee6aa5562' xxxx.log

    2.通过MDC设置traceId

    笔者目前遇到的项目,可以有三种情况去设置traceId。先简单的介绍MDC

     #MDC定义 Mapped Diagnostic Context,即:映射诊断环境。 MDC是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。 MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。 #MDC的使用方法 向MDC中设置值:MDC.put(key, value); 从MDC中取值:MDC.get(key); 将MDC中内容打印到日志中:%X{key}

    2.1 使用filter过滤器设置traceId

    新建一个过滤器,实现Filter,重写init,doFilter,destroy方法,设置traceId放在doFilter中,在destroy中调用MDC.clear()方法。

    @Slf4j@WebFilter(filterName = "traceIdFilter",urlPatterns = "/*")public class traceIdFilter implements Filter {        /**     * 日志跟踪标识     */    private static final String TRACE_ID = "TRACE_ID";        @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)            throws IOException, ServletException {        MDC.put(TRACE_ID, UUID.randomUUID().toString());        filterChain.doFilter(request, servletResponse);    }    @Override    public void destroy() {            MDC.clear();    }}

    2.2 使用JWT token过滤器的项目

    springboot项目经常使用spring security+jwt来做权限限制,在这种情况下,我们通过新建filter过滤器来设置traceId,那么在验证token这部分的日志就不会带上traceId,因此我们需要把代码放在jwtFilter中,如图:

    /** * token过滤器 验证token有效性 * * @author china */@Componentpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter {    @Autowired    private TokenService tokenService;    /**     * 日志跟踪标识     */    private static final String TRACE_ID = "TRACE_ID";    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)            throws ServletException, IOException {        MDC.put(TRACE_ID, UUID.randomUUID().toString());        LoginUser loginUser = tokenService.getLoginUser(request);        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {            tokenService.verifyToken(loginUser);            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));            SecurityContextHolder.getContext().setAuthentication(authenticationToken);        }        chain.doFilter(request, response);    }        @Override    public void destroy() {        MDC.clear();    }}

    2.3 使用Interceptor拦截器设置traceId

    定义一个拦截器,重写preHandle方法,在方法中通过MDC设置traceId

    /** * MDC设置traceId拦截器 * * @author china */@Componentpublic abstract class TraceIdInterceptor extends HandlerInterceptorAdapter {    private static final String UNIQUE_ID = "TRACE_ID";    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {        MDC.put(UNIQUE_ID, UUID.randomUUID().toString());        return true;    }        @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {        MDC.clear();    }}

    3.logback.xml中配置traceId

    与之前的相比只是添加了[%X{TRACE_ID}], [%X{***}]是一个模板,中间属性名是我们使用MDC put进去的。

    #之前#增加traceId后

    4.补充异步方法带入上下文的traceId

    异步方法会开启一个新线程,我们想要是异步方法和主线程共用同一个traceId,首先先新建一个任务适配器MdcTaskDecorator,如图:

    public class MdcTaskDecorator implements TaskDecorator     /**     * 使异步线程池获得主线程的上下文     * @param runnable     * @return     */    @Override    public Runnable decorate(Runnable runnable) {        Map map = MDC.getCopyOfContextMap();        return () -> {            try{                MDC.setContextMap(map);                runnable.run();            } finally {                MDC.clear();            }        };    }}

    然后,在线程池配置中增加executor.setTaskDecorator(new MdcTaskDecorator())的设置

     /** * 线程池配置 * * @author china **/@EnableAsync@Configurationpublic class ThreadPoolConfig {    private int corePoolSize = 50;    private int maxPoolSize = 200;    private int queueCapacity = 1000;    private int keepAliveSeconds = 300;    @Bean(name = "threadPoolTaskExecutor")    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setMaxPoolSize(maxPoolSize);        executor.setCorePoolSize(corePoolSize);        executor.setQueueCapacity(queueCapacity);        executor.setKeepAliveSeconds(keepAliveSeconds);        executor.setTaskDecorator(new MdcTaskDecorator());        // 线程池对拒绝任务(无线程可用)的处理策略        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        return executor;    }}

    最后,在业务代码上使用@Async开启异步方法即可

    @Async("threadPoolTaskExecutor")    void testSyncMethod();

    5.在接口放回中,增加traceId返回

    在笔者项目中,接口返回都使用了一个叫AjaxResult自定义类来包装,所以只需要把这个类的构造器中增加traceId返回即可,相对简单。

       /**     * 日志跟踪标识     */    private static final String TRACE_ID = "TRACE_ID";        /**     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。     */    public AjaxResult() {        super.put(TRACE_ID, MDC.get(TRACE_ID));    }    /**     * 初始化一个新创建的 AjaxResult 对象     *     * @param code 状态码     * @param msg  返回内容     */    public AjaxResult(int code, String msg) {        super.put(CODE_TAG, code);        super.put(MSG_TAG, msg);        super.put(TRACE_ID, MDC.get(TRACE_ID));    }    /**     * 初始化一个新创建的 AjaxResult 对象     *     * @param code 状态码     * @param msg  返回内容     * @param data 数据对象     */    public AjaxResult(int code, String msg, Object data) {        super.put(CODE_TAG, code);        super.put(MSG_TAG, msg);        super.put(TRACE_ID, MDC.get(TRACE_ID));        if (StringUtils.isNotNull(data)) {            super.put(DATA_TAG, data);        }    }

    以上是"Springboot+MDC+traceId日志中如何打印唯一traceId"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

    日志 线程 方法 内容 接口 过滤器 对象 项目 标识 篇文章 拦截器 跟踪 配置 上下 上下文 代码 任务 情况 文章 状态 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 耶加雪菲 互联网科技股 高科技互联网 重生之互联网科技帝国 互联网科技保险 互联网科技卖菜 伴你行互联网科技有限公司 郑州华宇互联网科技 衡水科技产业互联网平台 宁夏科技产业互联网案例 赶超互联网科技 微信多开 深圳市优智互联网科技有限公司 科技赋能视角下互联网保险发展 众安科技工业互联网合作 互联网金融科技公司的优势 广州千川互联网科技有限公司 富国互联网金融科技混合基金 上海创道互联网络科技有限公司 宇信科技在杭州互联网排第几 广州梦谷互联网科技有限公司领导 鑫亿盛互联网科技 狮子座互联网科技有限公司 广州善班互联网科技 佩信高科技互联网 老年人脱离互联网科技的作文 河南住房之家互联网科技 朱啸虎曾投资哪些互联网科技企业 无锡工业互联网科技有限公司 互联网金融运营模式中的金融科技 北京互联网科技类企业分布
    0