MDC 日志跟踪

MDC(Mapped Diagnostic Context)是 log4j 和 logback 日志框架提供的一个机制,可以在日志中添加上下文信息,以方便在日志中跟踪应用程序的执行流程和状态。下面是一个示例代码,展示了如何使用 MDC 实现日志跟踪:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class LoggingExample {
    private static final Logger logger = LoggerFactory.getLogger(LoggingExample.class);

    public static void main(String[] args) {
        MDC.put("transactionId", "123456789");
        logger.info("Starting transaction");
        try {
            // execute transaction
            logger.info("Transaction executed successfully");
        } catch (Exception e) {
            logger.error("Transaction failed", e);
        } finally {
            MDC.remove("transactionId");
            logger.info("Transaction finished");
        }
    }
}

在这个示例代码中,首先通过 MDC.put() 方法将一个名为 “transactionId” 的上下文变量设置为 “123456789”。然后使用 logger.info() 方法输出一条日志,该日志包含了 “Starting transaction” 的消息和 “transactionId” 上下文变量的值。接下来,通过 try-catch-finally 语句块执行一个模拟的事务,如果事务执行失败,则使用 logger.error() 方法输出一条错误日志。在 finally 语句块中,使用 MDC.remove() 方法将 “transactionId” 上下文变量从 MDC 中移除,然后使用 logger.info() 方法输出一条日志,该日志包含了 “Transaction finished” 的消息。

在这个示例代码中,使用 MDC.put() 方法将上下文变量设置为一个唯一的事务 ID,然后在日志中输出该事务 ID,以便在日志中跟踪该事务的执行流程和状态。可以在应用程序的其他地方设置其他的上下文变量,例如用户 ID、请求 URL、响应时间等,以便更全面地跟踪应用程序的执行流程和状态。

需要注意的是,使用 MDC 需要注意线程安全问题,因为 MDC 存储的上下文变量是线程局部的,不同的线程之间不能共享上下文变量。在多线程环境下使用 MDC 时,需要确保每个线程的上下文变量是独立的,以避免混淆和错误。


MDC 日志跟踪,基于springboot 拦截器 logback配置 线程局部变量,项目MDC最佳实战 项目示例代码

下面是一个基于 Spring Boot 的拦截器和 logback 配置的示例代码,展示了如何在项目中使用 MDC 实现日志跟踪,并给出了 MDC 最佳实践。

示例代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Configuration
public class LoggingInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
    private static final ThreadLocal<String> transactionIdThreadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String transactionId = UUID.randomUUID().toString();
        MDC.put("transactionId", transactionId);
        transactionIdThreadLocal.set(transactionId);
        logger.info("Request received: {} {}", request.getMethod(), request.getRequestURI());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("Request processed successfully");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        MDC.remove("transactionId");
        transactionIdThreadLocal.remove();
        logger.info("Request finished");
    }

    public static String getTransactionId() {
        return transactionIdThreadLocal.get();
    }
}

在这个示例代码中,定义了一个名为 LoggingInterceptor 的拦截器,该拦截器实现了 HandlerInterceptor 接口,包含了 preHandle()、postHandle() 和 afterCompletion() 三个方法。在 preHandle() 方法中,生成一个唯一的事务 ID,将其添加到 MDC 中,并使用 ThreadLocal 存储到当前线程的局部变量中,以便在后续的代码中获取该事务 ID。在 postHandle() 方法中,使用 logger.info() 方法输出一条日志,表示请求已成功处理。在 afterCompletion() 方法中,使用 MDC.remove() 方法将事务 ID 从 MDC 中移除,并使用 ThreadLocal.remove() 方法将其从当前线程的局部变量中移除,以确保线程安全和正确性。

logback 配置文件:

<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{transactionId} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.example" level="info"/>

    <root level="error">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

在这个 logback 配置文件中,与之前的示例代码相比,只是将占位符中的 “userId” 改为了 “transactionId”,以对应 MDC 中的事务 ID。

MDC 最佳实践:

  1. 在拦截器中使用 MDC 可以方便地对所有的请求添加上下文信息,例如请求 URL、请求方法、用户 ID等。可以将这些信息添加到 MDC 中,并在日志中输出,以便跟踪请求的执行流程和状态。

在拦截器中使用 ThreadLocal 存储事务 ID 可以避免在代码中传递事务 ID 的麻烦,并且可以保证线程安全和正确性。

在 logback 配置文件中使用占位符输出 MDC 中的上下文信息,可以使日志格式更加清晰和易读。

在使用 MDC 和 ThreadLocal 时,需要注意线程安全问题,确保每个线程的上下文信息和局部变量都是独立的,以避免混淆和错误。

如果在多个地方使用 MDC,可以将 MDC 的初始化和清理逻辑封装到一个公共的方法中,以便复用和管理。

最后,使用 MDC 和 ThreadLocal 时需要谨慎,考虑到性能和内存占用等因素。在项目中使用 MDC 时,需要根据具体情况进行调整和优化,以达到最佳的效果和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值