Springboot starter开发之traceId请求日志链路追踪

简介: 能标识一次请求的完整流程,包括日志打印、响应标识等,以便于出现问题可以快速定位并解决问题。

一、请求链路追踪是什么?



能标识一次请求的完整流程,包括日志打印、响应标识等,以便于出现问题可以快速定位并解决问题。


二、使用步骤



1. 相关知识点


ThreadLocal:一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。


MDC:(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能,基于ThreadLocal实现的一种工具类。


拦截器:基于拦截器对每个请求注入traceId。


2. 代码实现


1.封装TraceId工具类:


/**
 * @author yinfeng
 * @description traceId工具类
 * @since 2021/10/2 11:10
 */
public class TraceIdUtil {
    private static final String TRACE_ID = "traceId";
    public static void set() {
        MDC.put(TRACE_ID, generate());
    }
    public static String get() {
        return MDC.get(TRACE_ID);
    }
    public static void remove() {
        MDC.remove(TRACE_ID);
    }
    public static String generate() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
    }
}


2.springboot环境注入工具类

/**
 * @author yinfeng
 * @description 资源配置工具类
 * @since 2021/10/2 0:02
 */
public class PropertySourcesUtil {
    private static final String NAME = "aop.yinfeng";
    private static ConfigurableEnvironment environment;
    private static SpringApplication application;
    public static void setEnvironment(ConfigurableEnvironment environment) {
        if (PropertySourcesUtil.environment == null) {
            PropertySourcesUtil.environment = environment;
        }
    }
    public static SpringApplication getApplication() {
        return application;
    }
    public static void setApplication(SpringApplication application) {
        PropertySourcesUtil.application = application;
    }
    public static void set(String key, Object value) {
        getSourceMap().put(key, value);
    }
    public static Object get(String key) {
        return getSourceMap().get(key);
    }
    public static Map<String, Object> getSourceMap() {
        PropertySource<?> propertySource = environment.getPropertySources().get(NAME);
        Map<String, Object> source;
        if (propertySource == null) {
            source = new LinkedHashMap<String, Object>();
            propertySource = new MapPropertySource(NAME, source);
            environment.getPropertySources().addLast(propertySource);
        }
        source = (Map<String, Object>) propertySource.getSource();
        return source;
    }
}


3.支持配置的日志实体类:


/**
 * @author yinfeng
 * @description 日志配置类
 * @since 2021/10/1 17:45
 */
@Data
@ConfigurationProperties(prefix = "aop.logging")
public class LogProperties {
    private String logDir;
    // 因为logback和log4j的日志格式略有不同,所以提供2种打印格式
    private String logbackPattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} %X{traceId} %-5level %logger{30} : %msg%n";
    private String log4jPattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} %X{traceId} %-5level %clr{%-30.30c{1.}}{cyan} : %msg%n";
}

4.环境增强注入配置:


因为请求链路追踪在各个服务中比较常用,所以以starter的形式进行封装,在spring环境加载后进行配置注入。

/**
 * @author yinfeng
 * @description 环境注入抽象类
 * @since 2021/10/1 17:55
 */
public abstract class AbstractEnvironmentPostProcessor implements EnvironmentPostProcessor {
    private static final String DEV = "dev";
    private static final String STG = "stg";
    private static final String PRD = "prod";
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        PropertySourcesUtil.setEnvironment(environment);
        final List<String> profiles = Arrays.asList(environment.getActiveProfiles());
        if (profiles.contains(PRD)) {
            doPrd(environment, application);
        } else if (profiles.contains(STG)) {
            doStg(environment, application);
        } else {
            doDev(environment, application);
        }
        onProfile(environment, application);
    }
    protected void doPrd(ConfigurableEnvironment environment, SpringApplication application) {
    }
    protected void doStg(ConfigurableEnvironment environment, SpringApplication application) {
    }
    protected void doDev(ConfigurableEnvironment environment, SpringApplication application) {
    }
    protected void onProfile(ConfigurableEnvironment environment, SpringApplication application) {
    }
}


/**
 * @author yinfeng
 * @description 日志环境注入
 * @since 2021/10/1 17:52
 */
@EnableConfigurationProperties(LogProperties.class)
public class LogEnvAdvice extends AbstractEnvironmentPostProcessor {
    @Override
    protected void onProfile(ConfigurableEnvironment environment, SpringApplication application) {
        final Binder binder = Binder.get(environment);
        final BindResult<LogProperties> bindResult = binder.bind("aop.logging", Bindable.of(LogProperties.class));
        LogProperties logProperties = new LogProperties();
        if (bindResult.isBound()) {
            logProperties = bindResult.get();
        }
        // 配置日志打印格式
        if (isLogback(application)) {
            PropertySourcesUtil.set("logging.pattern.console", logProperties.getLogbackPattern());
            PropertySourcesUtil.set("logging.pattern.file", logProperties.getLogbackPattern());
            return;
        }
        PropertySourcesUtil.set("logging.pattern.console", logProperties.getLog4jPattern());
        PropertySourcesUtil.set("logging.pattern.file", logProperties.getLog4jPattern());
    }
    /**
     * 判断是否是logback日志格式
     *
     * @param application application
     * @return
     */
    private boolean isLogback(SpringApplication application) {
        final LoggingSystem loggingSystem = LoggingSystem.get(application.getClassLoader());
        return LogbackLoggingSystem.class.equals(loggingSystem.getClass());
    }
}


5.在spring.factory文件配置log环境注入类


org.springframework.boot.env.EnvironmentPostProcessor=com.yinfeng.common.enviroment.LogEnvAdvice


6.配置拦截器,在每个请求进入时注入traceId,因为基于threadLocal实现,所以需要在请求完成后进行手动清除,否则gc会扫描不到


/**
* @author yinfeng
* @description 日志拦截器
* @since 2021/10/2 11:09
*/
public class LogInterceptor implements HandlerInterceptor {
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
       TraceIdUtil.set();
       return true;
   }
   /**
    * 回收资源,防止oom
    * @param request
    * @param response
    * @param handler
    * @param ex
    * @throws Exception
    */
   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
       TraceIdUtil.remove();
   }
}


/**
 * @author yinfeng
 * @description 拦截器增强
 * @since 2021/10/2 11:15
 */
public class InterceptorAdvice implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
      // 将拦截器注入到容器中
        final InterceptorRegistration registration = registry.addInterceptor(new LogInterceptor()).order(Integer.MIN_VALUE);
        registration.addPathPatterns("/**");
    }
}


3. 测试一下效果


到此为止,通过traceId追踪请求链路代码基本完成,下面咱们来测认识一下


  1. 在pom文件中引入咱们的starter


<dependency>
    <groupId>com.yinfeng</groupId>
    <artifactId>common-starter</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
        </exclusion>
    </exclusions>
</dependency>


2..通过knife4j接口文档发送请求


image.png


3.查看日志:可以看到咱们所有的业务日志打印都会带上traceId,方便咱们快速定位问题


image.png


三、总结



下一节咱们来说对全局响应体包装和traceId链路追踪的结合。


相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
目录
相关文章
|
Java API 微服务
Java 21 与 Spring Boot 3.2 微服务开发从入门到精通实操指南
《Java 21与Spring Boot 3.2微服务开发实践》摘要: 本文基于Java 21和Spring Boot 3.2最新特性,通过完整代码示例展示了微服务开发全流程。主要内容包括:1) 使用Spring Initializr初始化项目,集成Web、JPA、H2等组件;2) 配置虚拟线程支持高并发;3) 采用记录类优化DTO设计;4) 实现JPA Repository与Stream API数据访问;5) 服务层整合虚拟线程异步处理和结构化并发;6) 构建RESTful API并使用Springdoc生成文档。文中特别演示了虚拟线程配置(@Async)和StructuredTaskSco
1292 0
|
供应链 JavaScript BI
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
这是一款专为小微企业打造的 SaaS ERP 管理系统,基于 SpringBoot+Vue+ElementUI+UniAPP 技术栈开发,帮助企业轻松上云。系统覆盖进销存、采购、销售、生产、财务、品质、OA 办公及 CRM 等核心功能,业务流程清晰且操作简便。支持二次开发与商用,提供自定义界面、审批流配置及灵活报表设计,助力企业高效管理与数字化转型。
972 2
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
864 7
|
人工智能 Java 数据库
飞算 JavaAI:革新电商订单系统 Spring Boot 微服务开发
在电商订单系统开发中,传统方式耗时约30天,需应对复杂代码、调试与测试。飞算JavaAI作为一款AI代码生成工具,专注于简化Spring Boot微服务开发。它能根据业务需求自动生成RESTful API、数据库交互及事务管理代码,将开发时间缩短至1小时,效率提升80%。通过减少样板代码编写,提供规范且准确的代码,飞算JavaAI显著降低了开发成本,为软件开发带来革新动力。
|
人工智能 自然语言处理 前端开发
20分钟上手DeepSeek开发:SpringBoot + Vue2快速构建AI对话系统
本文介绍如何使用Spring Boot3与Vue2快速构建基于DeepSeek的AI对话系统。系统具备实时流式交互、Markdown内容渲染、前端安全防护等功能,采用响应式架构提升性能。后端以Spring Boot为核心,结合WebFlux和Lombok开发;前端使用Vue2配合WebSocket实现双向通信,并通过DOMPurify保障安全性。项目支持中文语义优化,API延迟低,成本可控,适合个人及企业应用。跟随教程,轻松开启AI应用开发之旅!
|
缓存 NoSQL Java
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
1564 79
|
前端开发 JavaScript 程序员
鸿蒙开发:console日志输出
针对初学者而言,大家只需要掌握住日志打印即可,等到了鸿蒙应用开发的时候,还有一个鸿蒙原生的打印工具HiLog,到时,我们也会详细的去讲述,也会针对HiLog,封装一个通用的工具类。
599 11
鸿蒙开发:console日志输出
|
监控 Java 应用服务中间件
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
654 6
|
Java 应用服务中间件 API
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
本文介绍了 Spring Boot 的核心概念和使用场景,并通过一个实战项目演示了如何构建一个简单的 RESTful API。
497 5
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
885 2

热门文章

最新文章