首页 > 其他分享 >使用 SLF4J MDC 给日志添加上下文信息

使用 SLF4J MDC 给日志添加上下文信息

时间:2024-10-27 16:42:53浏览次数:4  
标签:Map MDC SLF4J 线程 key put 日志

SLF4J MDC(Mapped Diagnostic Context)可以帮助在日志中添加上下文信息,从而更好地跟踪和调试应用程序。MDC 允许你将特定于线程的键值对存储在日志上下文中,便于在日志中输出相关信息。

使用步骤

  1. 添加依赖:确保你的项目中已经包含了 SLF4J 和相关的日志实现(如 Logback 或 Log4j)。

  2. 设置 MDC:在代码中使用MDC.put(key, value)设置上下文信息。

  3. 记录日志:在日志消息中使用%X输出 MDC 中的内容。

  4. 清理 MDC:在处理完成后,使用MDC.clear()清理上下文,以避免内存泄漏。

示例代码

以下是一个简单的示例,展示如何使用 SLF4J MDC:

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

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

    public static void main(String[] args) {
        // 设置 MDC 值
        MDC.put("userId", "12345");
        MDC.put("transactionId", "abc-xyz");

        logger.info("This is a log message with MDC context");

        // 清理 MDC
        MDC.clear();
    }
}

日志输出

假设你使用 Logback,日志配置文件中可以定义输出格式如下:

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

    <root level="info">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

输出结果

运行上述代码后,日志输出可能类似于:

2024-10-27 02:57:00 [main] INFO  MdcExample - This is a log message with MDC context {userId=12345, transactionId=abc-xyz}

总结

通过使用 SLF4J MDC,你可以在日志中轻松地添加上下文信息,有助于在多线程环境中追踪问题。记得在使用完 MDC 后清理它,以保持良好的内存管理。

另,MDC 一般配合 AOP / Filter / Interceptor 使用:

@Around(value = "execution(* com.xx.xx.facade.impl.*.*(..))", argNames="pjp")
public Object validator(ProceedingJoinPoint pjp) throws Throwable {
  try {
    MDC.put("userId", "12345");
    MDC.put("transactionId", "abc-xyz");
    return pjp.proceed(args);
  } catch(Throwable e) {
    // 处理错误
  } finally {
    MDC.clear();
  }
}

源码

此处以 Logback 中的实现为例,只分析 MDC 的 put() 方法:

public class MDC {
  public static void put(String key, String val) throws IllegalArgumentException {
    if (key == null) {
      throw new IllegalArgumentException("key parameter cannot be null");
    }
    if (mdcAdapter == null) {
      throw new IllegalStateException("MDCAdapter cannot be null. See also "
          + NULL_MDCA_URL);
    }
    mdcAdapter.put(key, val);
  }
}

MDC 的 put() 方法利用 MDCAdapter 实现。

下面看一下 Logback 中 MDCAdapter 的实现 LogbackMDCAdapter:

public final class LogbackMDCAdapter implements MDCAdapter {
   final InheritableThreadLocal<Map<String, String>> copyOnInheritThreadLocal = new InheritableThreadLocal();

  public void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        } else {
            Map<String, String> oldMap = (Map)this.copyOnInheritThreadLocal.get();
            Integer lastOp = this.getAndSetLastOperation(1);
            if (!this.wasLastOpReadOrNull(lastOp) && oldMap != null) {
                oldMap.put(key, val);
            } else {
                Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
                newMap.put(key, val);
            }
        }
    }
}
  • 如上,LogbackMDCAdapter 有泛型为Map<String, String>的 InheritableThreadLocal,Map<String, String>被用来存储和当前线程相关的上下文信息,MDC 的 put() 方法会将值放在和当前线程关联的 Map 中。
  • MDC 内的键值对要能在调用链路中都能打印,那么 Map 肯定是存储在 ThreadLocal 中传递,从代码可以看到为 InheritableThreadLocal。
  • Map<String, String>存储在 InheritableThreadLocal 中,AOP 内真正的业务方法内部若进行了子线程的创建,MDC 内的键值对也能正常打印到日志中。但内部若是使用线程池的方式执行细分业务,则线程池任务内打印的日志则不会有此内容(线程池的 ThreadLocal 传递可以用阿里的 TransmittableThreadLocal)。

参考:ChatGPT、slf4j MDC 是个好东西

标签:Map,MDC,SLF4J,线程,key,put,日志
From: https://www.cnblogs.com/Higurashi-kagome/p/18508584

相关文章

  • 【SQL 性能分析的4种详细贯通方法】Com______;慢查询日志slow_query_log;profile;explain
    SQL性能分析是一个多维度的过程,涉及到对数据库操作的监控、诊断和优化。以下是使用四种方法详细且连贯地描述SQL性能分析的过程:1.Com_xxx计数器分析MySQL提供了Com_xxx状态变量,用于跟踪不同类型的SQL语句被执行的次数。这些计数器可以帮助我们了解数据库的使用模式和负......
  • Qt开发技巧(十九):定时器的调用问题,控件的透明问题,慎用事件过滤器,依赖库的路径链接,对话框
    继续讲一些Qt开发中的技巧操作:1.定时器的调用问题有一个场景经常遇到,那就是在符合某个条件下,延时一段时间去执行一段代码,如果短时间内触发多次又不需要频繁执行,只需要执行一次就行。如果选择用QTimer::singleShot无法终止已经触发的,这个时候就要主动实例化一个单次定时器,......
  • SLF4J 中的单例模式
    基于:SLF4J框架源码中是如何实现双重锁的?当我们使用SLF4J时,通常通过如下代码获取对应的Logger:Loggerlogger=LoggerFactory.getLogger(NoBindingTest.class);在LoggerFactory的getLogger方法中,最主要的功能就是获得Logger,获得Logger需要先获得对应的ILoggerFac......
  • SLF4J 中的适配器模式
    什么是适配器模式适配器模式中,适配器包装不兼容指定接口的对象,来实现不同兼容指定接口。SLF4J中的适配器模式SLF4J是一个日志门面系统,其中提供了统一的Logger等接口,许多框架都会面向SLF4J打印日志,这样就不会和具体的日志框架耦合在一起,框架使用者也就能够很方便地在不同......
  • java+vue计算机毕设电商平台日志分析系统的设计与实现【开题+程序+论文+源码】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展和电子商务行业的蓬勃兴起,电商平台已成为现代商业活动的重要组成部分。这些平台每天产生大量的用户行为数据、交易记录以及......
  • ELK日志管理系统搭建文档
    ELK日志管理系统搭建文档ELK是ElasticsearchLogstashkibana三个开源软件的组合来进行搭建.jianyi1jianyijianyi1建议架构:节点服务器部署程序备注节点1node-110.10.183.211Elasticsearch、kibana、head主节点节点2node-210.10.183.1......
  • 从门面模式到 SLF4J 及其 getLogger 方法原理
    基于以下内容总结:从门面模式到Slf4j、10分钟讲清楚JavaSLF4J,Java日志框架的扛把子,从原理到实践写后端接口的时候,先写一个Service接口,这个Service接口的实现中可能会调用多个其他Service或Mapper方法来实现某个业务,对于Controller,只需要传递参数给Service方法就......
  • 使用 NLP 和模式匹配检测、评估和编辑日志中的个人身份信息 - 第 2 部分
    作者:来自Elastic StephenBrown如何使用Elasticsearch、NLP和模式匹配检测、评估和编辑日志中的PII。简介:分布式系统中高熵日志的普遍存在大大增加了PII(PersonallyIdentifiableInformation-个人身份信息)渗入我们日志的风险,这可能导致安全和合规性问题。这篇由两......
  • 一文教会你如何使用 iLogtail SPL 处理日志
    作者:阿柄随着流式处理的发展,出现了越来越多的工具和语言,使得数据处理变得更加高效、灵活和易用。在此背景下,SLS推出了SPL(SLSProcessingLanguage)语法,以此统一查询、端上处理、数据加工等的语法,保证了数据处理的灵活性。iLogtail作为日志、时序数据采集器,在2.0版本中,全面......
  • 麒麟操作系统中的系统日志如何帮助你了解电脑运行情况
    ​麒麟操作系统中的系统日志是了解电脑运行状况的关键工具:1.识别并定位系统问题;2.监测系统运行趋势;3.审计系统安全状况;4.优化电脑性能;5.理解应用程序行为。了解日志的基础知识是向高效问题解决迈出的第一步。1.识别并定位系统问题麒麟操作系统的日志记录包含了系统启动、运行......