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); (编辑:甘南站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |