mdc 全局标识-实现日志链路追踪

简述

继之前说的《spring aop 自动打印接口的出入参日志,更专注于业务逻辑》,日志乃是我们排查问题的重要且主要数据之一,一个请求的完整日志能够帮我们快速定位以及分析问题的原因;我们本地跑程序,或许日志基本都会按照代码逻辑一行行的打印到终端或者文件,但测试或生产服务器环境运行的公开的多用户访问的程序,基本不会有这么有序的日志呀,基本都是多个请求的日志穿插打印,排查问题难以区分哪些参数是对应哪个请求的,哪些参数是第一哪个SQL的;所以,我们需要一个全局唯一的、贯穿整个请求业务逻辑的标识,今天我们就来讲讲MDC实现全局日志链路追踪。

(我们目前的说文都是基于spring boot项目开讲的)

1、新建一个web项目 springboot-logback-async

pom文件:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.4.8
         
    
    com.example
    springboot-logback-async
    0.0.1-SNAPSHOT
    springboot-logback-async
    Demo project for Spring Boot
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.projectlombok
            lombok
            1.18.20
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

今天这个打印日志用到了logBack,spring boot自带了logBack的依赖,感兴趣的道友们点击 spring-boot-starter 进入查看源码依赖。

2、新建一个过滤器 MyFilter.java,在过滤器里设置全局唯一标识

今天打印日志我们从过滤器开始,不了解的道友可以稍后去看另一篇文章《spring boot 注解实现自定义Filter》。我们使用UUID作为全局唯一标识。这一步是重点。

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.UUID;

@Slf4j
@WebFilter(filterName = "myFilter", urlPatterns = "/*", servletNames = "*")
@Order(1)
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
      log.info("初始化我的过滤器。。。。。。。。");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //设置全局唯一标识,这里使用UUID作为唯一标识
        MDC.put("traceId", UUID.randomUUID().toString().replace("-",""));
        //放行前可在这里做一些操作
        log.info("doFilter进入过滤器操作方法。。。");
        chain.doFilter(request, response);
        // 请求返回可在这里做一些操作
        log.info("doFilter准备离开过滤器操作。。。");
        //马上返回前端,这里清除标识,避免内存资源浪费
        MDC.remove("traceId");
    }
}

3、新建一个测试接口 InitRest.java、service类,为了观看直观,我就用到了service层。

import com.example.demo.service.HelloService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class InitRest {

    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello() {
        log.info("进入接口。。。");
        return helloService.hello();
    }
}
public interface HelloService {
    String hello();
}
import com.example.demo.service.HelloService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class HelloServiceImpl implements HelloService {

    @Override
    public String hello() {
        log.info("进入service方法,做业务处理。。。。");
        tranDto();
        return "返回一个字符串";
    }

    private void tranDto() {
        log.info("方法调用方法tranDto。。。。");
        tranEntity();
    }
    private void tranEntity() {
        log.info("方法再调用方法tranEntity。。。。");
    }
}

4、新建一个logback.xml 文件,用于配置日志输出

我直接粘代码了,里面的语法不了解的可以先直接忽略哦,主要是是使用 %X{traceId} 这个变量打印全局唯一标识的值。



    
    
    
        
            %d [%t] [%X{traceId}] [%c] [%p] (%file:%line\)- %m%n
            UTF-8
        
    
    
    
        log/run.log
        
            log/run.log.%d.%i
            
                
                64 MB
            
            
            7
        
        
            
                %d [%t] [%X{traceId}] [%c] [%p] - %m%n
            
            UTF-8 
        
    

    
        
    
    
    
        
    

5、启动类加一个注解,OK,启动项目,访问接口,注意看日志。

@SpringBootApplication
//启用过滤器
@ServletComponentScan(basePackageClasses = {MyFilter.class})
public class SpringbootLogbackAsyncApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootLogbackAsyncApplication.class, args);
    }
}
目录结构
启动成功
请求接口
标准输出打印全局标识
日志文件

OK,完美实现。

但,今天写这篇文章的重点,我却是另有想法,上面的做法可以解决大部分的日志混乱问题了,但是它的全局标识值只局限在当前线程,若产生子线程,则子线程的日志将缺失该标识,我苦思冥想也整不出什么好办法,希望各位道友们集思广益,帮忙出出idea,非常欢迎给我留言,抛出你的思路。

好了,若觉得文章还不错,欢迎给我留言点赞加转发!!!

你可能感兴趣的:(mdc 全局标识-实现日志链路追踪)