Spring项目如何实现带请求链路id的日志记录

 更新时间:2024年12月19日 08:49:12   作者:名一  
我们在做项目的时候通常需要通过请求日志来排查定位线上问题,如果日志比较多而我们又需要查找整个请求的全部日志的时候会比较困难,下面我们就来看看如何用java aop实现请求id的日志记录吧

我们在做java项目的时候通常需要通过请求日志来排查定位线上问题,在日志比较多而我们又需要查找整个请求的全部日志的时候会比较困难。所以,就需要在日志记录的时候讲同一个请求的关键日志用同一个唯一标识串联起来。这样查找的时候就会比较好查找。下面来用java aop实现请求id的日志记录。(该支持子线程继承主线程请求id)

一:首先我们需要一个日志请求链路id切面类

注意:如果不考虑多线程则(第二步和第三步可以不要)

package com.iMagine.iMagine_pro.aop;

import com.iMagine.iMagine_common.utils.UUIDUtil;
import com.iMagine.iMagine_pro.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;


/**
 * @author 名一
 * @ClassName TraceIdAspect
 * @description: 日志请求链路id切面处理
 * @datetime 2024年 12月 16日 14:28
 * @version: 1.0
 */
@Slf4j
@Aspect
@Component
public class TraceIdAspect {
    
    /** 链路追踪id */
    public final static String TRACE_ID = "TRACE_ID";
    /** 用户 */
    public final static String USER = "USER";
    
    /**
     * 链路id切点定义
     */
    @Pointcut("execution(* com.iMagine.iMagine_pro.controller.*.*(..))")
    public void TraceIdCut() {

    }

    /**
     * 链路id添加
     */
    @Before("TraceIdCut()")
    public void cutProcessBefore() {
        MDC.put(TRACE_ID, UUIDUtil.getUUID());
        //以下代码为记录用户信息,方便更直观的识别日志操作人信息。若不需要可以将下面部分删除
        String nickname = TokenUtil.getNicknameByToken();
        if (null == nickname){
            nickname = "游客访问";
        }
        MDC.put(USER, nickname);
    }

    /**
     * 链路id清除
     */
    @After("TraceIdCut()")
    public void cutProcessAfter() {
        MDC.clear();
    }
}
package com.iMagine.iMagine_common.utils;

import java.util.UUID;

/**
 * @author 名一
 * @ClassName UUIDUtil
 * @description: UUID工具类
 * @datetime 2024年 04月 23日 11:49
 * @version: 1.0
 */
public class UUIDUtil {

    /**
     * 获取UUID
     *
     * @return
     */
    public static String getUUID() {
        //生产uuid并去掉uuid的短横线
        return UUID.randomUUID().toString().replace("-", "");
    }
}

二:创建一个处理多线程链路追踪的工具类

package com.iMagine.iMagine_common.utils;

import com.iMagine.iMagine_common.constant.SysConstant;
import org.slf4j.MDC;

import java.util.Map;

/**
 * @author 名一
 * @ClassName ThreadMdcUtil
 * @description: 多线程链路追踪工具类
 * @datetime 2024年 12月 16日 14:57
 * @version: 1.0
 */
public class ThreadMdcUtil {

    // 获取唯一性标识
    public static String generateTraceId() {
        return UUIDUtil.getUUID();
    }

    public static void setTraceIdIfAbsent() {
        if (MDC.get(SysConstant.TRACE_ID) == null) {
            MDC.put(SysConstant.TRACE_ID, generateTraceId());
        }
    }

    /**
     * 用于父线程向线程池中提交任务时,将自身MDC中的数据复制给子线程
     *
     * @param runnable  要执行的线程
     * @param context   父线程的mdc
     * @return          链路id传递后的任务
     */
    public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
        return () -> {
            if (context == null) {
                MDC.clear();
            } else {
                MDC.setContextMap(context);
            }
            setTraceIdIfAbsent();
            try {
                runnable.run();
            } finally {
                MDC.clear();
            }
        };
    }
}

三:在往线程池放任务的时候做请求链路id传递

/**
 * 线程池添加 [ai server 相关操作(psot)]任务
 *
 * @param task 添加的任务
 */
public static void addSendPostThreadToThreadPool(SendPostThread task) {
    log.info("addSendPostThreadToThreadPool pool:{}, task:{}", pool, task);
    try {
        pool.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    } catch (Exception e) {
        log.error("addSendPostThreadToThreadPool error pool:{}, task:{}, e:", pool, task, e);
    }
}

四:在日志配置里将链路追踪id加入日志输出格式的配置里

<!-- 日志输出格式 (其中%X{TRACE_ID}是mdc里边链路请求id的key,user-%X{USER}是用户信息的key)-->
<property name="ENCODER_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %X{TRACE_ID} user-%X{USER} %-5level %logger{32}-[%line] - %msg%n"/>

到此spring 项目实现带请求链路id的日志记录就完成了

到此这篇关于Spring项目如何实现带请求链路id的日志记录的文章就介绍到这了,更多相关Spring请求id日志记录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Swagger注解-@ApiModel和@ApiModelProperty的用法

    Swagger注解-@ApiModel和@ApiModelProperty的用法

    这篇文章主要介绍了Swagger注解-@ApiModel和@ApiModelProperty的用法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • java 读写锁的使用及它的优点

    java 读写锁的使用及它的优点

    这篇文章主要介绍了java 读写锁的使用及它的优点,读写锁的特点就是是读读不互斥、读写互斥、写写互斥,下面具体使用分享需要的小伙伴可以参考一下
    2022-05-05
  • 详解 Java Maximum redirects (100) exceeded

    详解 Java Maximum redirects (100) exceeded

    这篇文章主要介绍了详解 Java Maximum redirects (100) exceeded的相关资料,需要的朋友可以参考下
    2017-05-05
  • 详解如何给SpringBoot部署的jar包瘦身

    详解如何给SpringBoot部署的jar包瘦身

    这篇文章主要介绍了如何给SpringBoot部署的jar包瘦身,如今迭代发布是常有的事情,每次都上传一个如此庞大的文件,会浪费很多时间,接下来小编就以一个小项目为例,来演示如何给jar包瘦身,需要的朋友可以参考下
    2023-07-07
  • 从HelloWorld和文档注释开始入门Java编程

    从HelloWorld和文档注释开始入门Java编程

    这篇文章主要介绍了从HelloWorld和文档注释开始入门Java编程,涉及到Javadoc工具的使用,需要的朋友可以参考下
    2015-10-10
  • Java模板动态生成word文件的方法步骤

    Java模板动态生成word文件的方法步骤

    最近项目中需要根据模板生成word文档,模板文件也是word文档。本文使用使用freemarker模板生成word文件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • 使用 EasyCode生成springboot+mybatis基础程序的实现示例

    使用 EasyCode生成springboot+mybatis基础程序的实现示例

    本文主要介绍了使用 EasyCode生成springboot+mybatis基础程序的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • RocketMQ实现随缘分BUG小功能示例详解

    RocketMQ实现随缘分BUG小功能示例详解

    这篇文章主要为大家介绍了RocketMQ实现随缘分BUG小功能示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • SpringBoot整合UEditor的示例代码

    SpringBoot整合UEditor的示例代码

    本篇文章主要介绍了SpringBoot整合UEditor的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02
  • 深入理解Java 对象和类

    深入理解Java 对象和类

    下面小编就为大家带来一篇深入理解Java 对象和类。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05

最新评论