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实时生图绘板
目录
相关文章
|
3月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
631 5
|
9月前
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
2203 1
|
9月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录——使用Logger在项目中打印日志
本文介绍了如何在项目中使用Logger打印日志。通过SLF4J和Logback,可设置不同日志级别(如DEBUG、INFO、WARN、ERROR)并支持占位符输出动态信息。示例代码展示了日志在控制器中的应用,说明了日志配置对问题排查的重要性。附课程源码下载链接供实践参考。
1078 0
|
9月前
|
SQL Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— application.yml 中对日志的配置
在 Spring Boot 项目中,`application.yml` 文件用于配置日志。通过 `logging.config` 指定日志配置文件(如 `logback.xml`),实现日志详细设置。`logging.level` 可定义包的日志输出级别,例如将 `com.itcodai.course03.dao` 包设为 `trace` 级别,便于开发时查看 SQL 操作。日志级别从高到低为 ERROR、WARN、INFO、DEBUG,生产环境建议调整为较高级别以减少日志量。本课程采用 yml 格式,因其层次清晰,但需注意格式要求。
868 0
|
9月前
|
Java API 开发者
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录——slf4j 介绍
在软件开发中,`System.out.println()`常被用于打印信息,但大量使用会增加资源消耗。实际项目推荐使用slf4j结合logback输出日志,效率更高。Slf4j(Simple Logging Facade for Java)是一个日志门面,允许开发者通过统一方式记录日志,无需关心具体日志系统。它支持灵活切换日志实现(如log4j或logback),且具备简洁占位符和日志级别判断等优势。阿里巴巴《Java开发手册》强制要求使用slf4j,以保证日志处理方式的统一性和维护性。使用时只需通过`LoggerFactory`创建日志实例即可。
681 0
|
5月前
|
机器学习/深度学习 XML Java
【spring boot logback】日志logback格式解析
在 Spring Boot 中,Logback 是默认的日志框架,它支持灵活的日志格式配置。通过配置 logback.xml 文件,可以定义日志的输出格式、日志级别、日志文件路径等。
937 5
|
8月前
|
JSON API 数据格式
【Azure APIM】如何把APIM中处理的请求的所有请求头保存在日志中?
Azure API Management 默认诊断日志不记录请求的 Header 和 Body 信息。为实现记录,可通过配置 Trace 策略解决。例如,使用 `context.Request.Headers` 和 `context.Request.Body` 获取相关信息,并以 JSON 或字符串格式保存。示例代码展示了如何将 Headers 转换为 JSON 或逗号分隔字符串形式记录。相关参考资料包括 Set Body Policy 和 Trace Policy 官方文档,帮助进一步了解与扩展功能。
212 36
|
11月前
|
开发框架 运维 监控
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
448 5
|
11月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
849 8
|
11月前
|
存储 Prometheus Cloud Native
分布式系统架构6:链路追踪
本文深入探讨了分布式系统中的链路追踪理论,涵盖追踪与跨度的概念、追踪系统的模块划分及数据收集的三种方式。链路追踪旨在解决复杂分布式系统中请求流转路径不清晰的问题,帮助快速定位故障和性能瓶颈。文中介绍了基于日志、服务探针和边车代理的数据收集方法,并简述了OpenTracing、OpenCensus和OpenTelemetry等链路追踪协议的发展历程及其特点。通过理解这些概念,可以更好地掌握开源链路追踪框架的使用。
1183 41

热门文章

最新文章