其他分享
首页 > 其他分享> > MDC日志追踪

MDC日志追踪

作者:互联网

日志追踪

1.如何实现 问题 以及解决方案

初级 每层传参下去 从controller -> service -> dao

中级 通过threalocal

什么是threadlocal 为线程创建一个副本 是以线程号为id 的map 可以塞值 塞我们想要的 traceId

框架MDC

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

MDC中的put方法其实就是讲键值对放入一个Hashtable对象中,然后赋值给当前线程的ThreadLocal.ThreadLocalMap对象,即threadLocals,这保证了各个线程的在MDC键值对的独立性。

边界

1.mdc争对的是当前线程 ,所以当你使用异步,或者说new Thread的时候就不支持了

如何解决: 可以在多线程里面从新设置一遍 MDC.put(“traceId”,“业务唯一键”)

2.分布式的时候 当A系统 调用B系统的时候 下游的系统如何获取traceId

如何解决 从A系统到B系统 走的是hettp 协议 所以我们就可以在url上面做文章

hettp://www.bai.A?traceId=唯一键

springcloud里面的sleuth 就是通过这个方法去实现的

我们直接的服务 怎么去实现他呢 编写两个filter 去实现他

转发的时候对url里面设置一个这个的请求参数

消费端那边先去获取这个traceId 再通过mdc put进去 日志打印的格式加上traceID 这个参数

3.日志采集 服务很多 不知你哪一台去获取 elk 后续再涉及

代码实现

pom文件

<!--一定要先把这里面的logging给排除掉-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

application.properties

#指定log的配置文件
logging.config=classpath:log4j2.xml

log4j2.xml

%X{name}会查询设置到MDC中的变量 这个非常重要

%d{yyyy-MM-dd HH:mm:ss,SSS} %t %X{traceId} %-5p %c{1}:%L -%m%n

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!-- status log4j2内部日志级别 -->
<configuration status="INFO">
    <!-- 全局参数  %X{name}会查询设置到MDC中的变量 -->
    <Properties>
        <Property name="pattern">%d{yyyy-MM-dd HH:mm:ss,SSS} %t %X{traceId} %-5p %c{1}:%L -%m%n</Property>
        <Property name="displayName">EurekaServer</Property>
    </Properties>
    <Appenders>
        <Console name="console" target="SYSTEM_OUT" follow="true">
            <PatternLayout>
                <pattern>${pattern}</pattern>
            </PatternLayout>
        </Console>
        <!-- 文件 每次运行程序会自动清空,由append属性决定 -->
        <File name="error" fileName="${displayName}_error.log" append="false">
            <!-- 指定error 级别的日志 -->
            <ThresholdFilter level="ERROR" onMatch="ACCEPT"
                             onMismatch="DENY" />
            <PatternLayout>
                <pattern>${pattern}</pattern>
            </PatternLayout>
        </File>
        <!-- 滚动文件 -->
        <RollingFile name="rollingFile" fileName="${displayName}.log"
                     filePattern="${displayName}_%d{yyyy-MM-dd}.log">
            <PatternLayout>
                <pattern>${pattern}</pattern>
            </PatternLayout>
            <!-- 按大小划分 -->
            <SizeBasedTriggeringPolicy size="50 MB" />
        </RollingFile>
    </Appenders>
    <Loggers>
        <!-- <Logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"
            /> <Logger nabaime="org.apache.coyote.http11.Http11NioProtocol" level="WARN"
            /> <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"
            /> -->
        <Logger name="org.springframework" level="WARN" />
        <Logger name="com.bai" level="DEBUG" />
        <Root level="DEBUG">
            <AppenderRef ref="console"></AppenderRef>
            <AppenderRef ref="error"></AppenderRef>
            <AppenderRef ref="rollingFile"></AppenderRef>
        </Root>
    </Loggers>
</configuration>

编写任意一个拦截器

import org.slf4j.MDC;
/**
 * @Author BAI
 * @Description 语言转化拦截器
 */
@Slf4j
public class MyLocaleChangeInterceptor implements HandlerInterceptor {
    public static final String TRACE_ID ="traceId";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
       // 使用mdc塞值  添加日志追踪
        String traceId = UUID.randomUUID().toString().replace("-","");
        MDC.put(TRACE_ID, traceId);
        // 这个添加的操作一定要在preHandle 一般误区会以为会在postHandle
        // 但是使用的时候会发现 在使用postHandle 之前 已经返回给前端
        response.addHeader(TRACE_ID,traceId);
        I18nLocalResolver i18NLocalResolver = new I18nLocalResolver();
        Locale locale = i18NLocalResolver.resolveLocale(request);
        i18NLocalResolver.setLocale(request,response,locale);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion start...");
        MDC.remove(TRACE_ID);
        log.info("afterCompletion end...");
    }
}

线程池或者异步任务简单处理

String traceId = MDC.get("traceId");
for (int i = 0; i < 1000; i++) {
    newFixedThreadPool(1000).execute(()->{
        MDC.put("traceId",traceId);
        log.info("stu:",stu.getOperateEnum());
        MDC.remove("traceId");
    });
}

标签:traceId,MDC,boot,线程,put,日志,追踪
来源: https://blog.csdn.net/weixin_42899433/article/details/113764306