加入收藏 | 设为首页 | 会员中心 | 我要投稿 甘南站长网 (https://www.0941zz.com/)- 科技、行业物联网、开发、云计算、云管理!
当前位置: 首页 > 编程开发 > Java > 正文

Slf4j MDC机制

发布时间:2023-02-16 13:50:47 所属栏目:Java 来源:互联网
导读:MDC 简介 MDC ( Mapped Diagnostic Contexts ),它是一个线程安全的存放诊断日志的容器。 Logback设计的一个目标之一是对分布式应用系统的审计和调试。在现在的分布式系统中,需要同时处理很多的请求。如何来很好的区分日志到底是那个请求输出的呢?我们可以

   <filter-class>
     ch.qos.logback.classic.helpers.MDCInsertingServletFilter
   </filter-class>
  </filter>
  <filter-mapping>
   <filter-name>MDCInsertingServletFilter</filter-name>
   <url-pattern>/*</url-pattern>
  </filter-mapping>
  应用key:
 
  
 
  %X{req.remoteHost} %X{req.requestURI}%n%d - %m%n
  管理每个线程的MDC容器
  我们在主线程上,新起一个子线程,并由 java.util.concurrent.Executors来执行它时,在早期的版本中子线程可以直接自动继承父线程的MDC容器中的内容,因为MDC在早期版本中使用的是InheritableThreadLocal来作为底层实现。但是由于性能问题被取消了,最后还是使用的是ThreadLocal来作为底层实现。这样子线程就不能直接继承父线程的MDC容器。
 
  所以,Logback官方建议我们在父线程新建子线程之前调用MDC.getcopyOfContextMap()方法将MDC内容取出来传给子线程,子线程在执行操作前先调用MDC.setContextMap()方法将父线程的MDC内容设置到子线程。
 
  Slf4j MDC实现原理
 
 
  MDC.java
 
  Slf4j 的实现原则就是调用底层具体实现类,比如logback,logging等包;而不会去实现具体的输出打印等操作。这里使用了装饰者模式,看源码就能看出来,所有的方法都是在对mdcAdapter 这个属性进行操作。所以实现核心是MDCAdapter类。
 
  MDCAdapter
  MDCAdapter只是定义了一个接口,具体实现由子类完成,源码如下:
 
  
 
  public interface MDCAdapter {
      public void put(String key,String val);
      public String get(String key);
      public void remove(String key);
      public void clear();
      public Map<String,String> getcopyOfContextMap();
      public void setContextMap(Map<String,String> contextMap);
  }
  它有三个实现类,BasicMDCAdapter、LogbackMDCAdapter,nopMDCAdapter。Logback使用的是LogbackMDCAdapter。
 
  LogbackMDCAdapter
  
 
  package ch.qos.logback.classic.util;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Set;
  import org.slf4j.spi.MDCAdapter;
  public class LogbackMDCAdapter implements MDCAdapter {
      final ThreadLocal<Map<String,String>> copyOnThreadLocal = new ThreadLocal<Map<String,String>>();
      private static final int WRITE_OPERATION = 1;
      private static final int MAP_copY_OPERATION = 2;
      // keeps track of the last operation performed
      final ThreadLocal<Integer> lastOperation = new ThreadLocal<Integer>();
      private Integer getAndSetLastOperation(int op) {
          Integer lastOp = lastOperation.get();
          lastOperation.set(op);
          return lastOp;
      }
      private boolean wasLastOpReadOrNull(Integer lastOp) {
          return lastOp == null || lastOp.intValue() == MAP_copY_OPERATION;
      }
      private Map<String,String> duplicateAndInsertNewMap(Map<String,String> oldMap) {
          Map<String,String> newMap = Collections.synchronizedMap(new HashMap<String,String>());
          if (oldMap != null) {
              // we don't want the parent thread modifying oldMap while we are
              // iterating over it
              synchronized (oldMap) {
                  newMap.putAll(oldMap);
              }
          }
          copyOnThreadLocal.set(newMap);
          return newMap;
      }
      /**
       * Put a context value (the <code>val</code> parameter) as identified with the
       * <code>key</code> parameter into the current thread's context map. Note that
       * contrary to log4j,the <code>val</code> parameter can be null.
       * <p/>
       * <p/>
       * If the current thread does not have a context map it is created as a side
       * effect of this call.
       *
       * @throws IllegalArgumentException in case the "key" parameter is null
       */
      public void put(String key,String val) throws IllegalArgumentException {
          if (key == null) {
              throw new IllegalArgumentException("key cannot be null");
          }
          Map<String,String> oldMap = copyOnThreadLocal.get();
          Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);
          if (wasLastOpReadOrNull(lastOp) || oldMap == null) {
              Map<String,String> newMap = duplicateAndInsertNewMap(oldMap);
              newMap.put(key,val);
          } else {
              oldMap.put(key,val);
          }
      }
      /**
       * Remove the the context identified by the <code>key</code> parameter.
       * <p/>
       */
      public void remove(String key) {
          if (key == null) {
              return;
          }
          Map<String,String> oldMap = copyOnThreadLocal.get();
          if (oldMap == null)
              return;
          Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);
          if (wasLastOpReadOrNull(lastOp)) {
              Map<String,String> newMap = duplicateAndInsertNewMap(oldMap);
              newMap.remove(key);
          } else {
              oldMap.remove(key);

(编辑:甘南站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读