美文网首页
mdc 全局标识-实现日志链路追踪

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

作者: 技术指北 | 来源:发表于2021-06-28 23:14 被阅读0次

简述

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

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

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

pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-logback-async</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-logback-async</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- 构建成可运行的Web项目 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

今天这个打印日志用到了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} 这个变量打印全局唯一标识的值。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- %d日期,%t线程名,%c类的全名,%p日志级别,%file文件名,%line行数,%m%n输出的信息 -->
    <!-- 控制台输出配置 -->
    <appender name="stdout"
        class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d [%t] [%X{traceId}] [%c] [%p] (%file:%line\)- %m%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 日志文件配置 -->
    <appender name="baselog"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>log/run.log</File>
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>log/run.log.%d.%i</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 每一个日志文件最大size -->
                <maxFileSize>64 MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 保留天数 -->
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>
                %d [%t] [%X{traceId}] [%c] [%p] - %m%n
            </pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="stdout" />
    </root>
    <!-- 定义日志输出的package包名 -->
    <logger name="com.example" level="DEBUG">
        <appender-ref ref="baselog" />
    </logger>
</configuration>

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

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

OK,完美实现。

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

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

相关文章

网友评论

      本文标题:mdc 全局标识-实现日志链路追踪

      本文链接:https://www.haomeiwen.com/subject/jsncultx.html