浅谈 logback的MDC机制

1 篇文章 0 订阅

logback的MDC机制

1. MDC 介绍:

​ MDC(Mapped Diagnostic Context,映射调试上下文),即将一些运行时的上下文数据通过logback打印出来,是 一种方便在多线程条件下记录日志的功能。和SiftingAppender一起,可以实现根据运行时的上下文数据,将日志保存到不同的文件中。

MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线程池中复用已有的线程。在一个用户的会话存续期间,可能有多个线程处理过该用户的请求。这使得比较难以区分不同用户所对应的日志。当需要追踪某个用户在系统中的相关日志记录时,就会变得很麻烦。

  一种解决的办法是采用自定义的日志格式,把用户的信息采用某种方式编码在日志记录中。这种方式的问题在于要求在每个使用日志记录器的类中,都可以访问到用户相关的信息。这样才可能在记录日志时使用。这样的条件通常是比较难以满足的。MDC 的作用是解决这个问题。

  MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

为了验证MDC的正确性,我写了个简单的多线程程序,代码如下:

import org.apache.log4j.MDC;  
  
public class ThreadTest extends Thread {  
    private int i ;  
      
    public ThreadTest(){  
    }  
      
    public ThreadTest(int i){  
        this.i = i;  
    }  
      
    public void run(){  
        System.out.println(++i);  
        MDC.put("username", i);  
      
        for (int j = 0; j < 100; j++) {  
            System.out.println("aaa" + i);  
            if(j==10){  
                try {  
                    this.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
        System.out.println("run: " + i + "     "  + MDC.get("username"));  
    }  
      
    public static void main(String args[]) throws InterruptedException{  
        ThreadTest t1 = new ThreadTest(1);  
        t1.start();  
        ThreadTest t2 = new ThreadTest(2);  
        t2.start();  
    }  
}  

输出结果是:

2  
3  
aaa3  
aaa3  
aaa2  
aaa3  
aaa2  
aaa3  
aaa2  
aaa3  
aaa2  
aaa3  
aaa2  
aaa3  
aaa2  
aaa2  
aaa2  
aaa2  
aaa2  
run: 2     2  
aaa3  
aaa3  
aaa3  
run: 3     3  

 

从结果中可以看出:进程t1与t2在MDC中的值是没有相互影响的,确保了多进程下进程之间在MDC存放的值是没有相互的影响的或者说是无关的(进程t1在MDC中的username的键值为2;进程t2在MDC中的username的键值为3)。
分析:

  MDC类put方法:

public static void put(String key, Object o)  
  {  
    mdc.put0(key, o);  
  }  
  
  private void put0(String key, Object o)  
  {  
    if (this.java1) {  
      return;  
    }  
    Hashtable ht = (Hashtable)((ThreadLocalMap)this.tlm).get();  
    if (ht == null) {  
      ht = new Hashtable(7);  
      ((ThreadLocalMap)this.tlm).set(ht);  
    }  
    ht.put(key, o);  
  }  

结合类java.lang.ThreadLocal<T>及Thread类可以知道,MDC中的put方法其实就是讲键值对放入一个Hashtable对象中,然后赋值给当前线程的ThreadLocal.ThreadLocalMap对象,即threadLocals,这保证了各个线程的在MDC键值对的独立性。

下边为java.lang.ThreadLocal<T>的部分代码:

public class ThreadLocal<T> {  
      
    public void set(T value) {  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null)  
            map.set(this, value);  
        else  
            createMap(t, value);  
    }  
  
  
    public T get() {  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null) {  
            ThreadLocalMap.Entry e = map.getEntry(this);  
            if (e != null)  
                return (T)e.value;  
        }  
        return setInitialValue();  
    }  
  
    ThreadLocalMap getMap(Thread t) {  
        return t.threadLocals;  
    }  
      
}  

Thread类的部分代码:

public class Thread implements Runnable {  
    ThreadLocal.ThreadLocalMap threadLocals = null;  
  
    ......................  
    .........................  
  
    static class ThreadLocalMap { //ThreadLocalMap为Thread类的内部类  
  
    }  
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值