- 一 What's MDC
- 1.1 本质
- 1.2 例子
- 二 Automating access to the MDC
- 2.1 案例一
- 2.2 切线程
- 2.3 MDCInsertingServletFilter
- 三 参考
MDC (Mapped Diagnostic Context) 一 What’s MDC
在分布式系统中,多线程处理多个客户端司空见惯.有个做法是每个客户端对应一个Logger,但这种做法会产生大量Logger而且管理起来也不便.另有一种做法是将日志的上下文信息放到MDC里.
MDC 是slf4j的定义,而不是 Logback的, 但Logback-classic依赖了 slf4j,所以Logback中也能使用slf4j.
但是 slf4j中的MDC 只是个 代理, Logback中真正的MDC实现是LogbackMDCAdapter.
这是MDC的核心方法, 其实也是些增删改查的方法.
public class MDC { //Put a context value as identified by key //into the current thread's context map. public static void put(String key, String val); //Get the context identified by the key parameter. public static String get(String key); //Remove the context identified by the key parameter. public static void remove(String key); //Clear all entries in the MDC. public static void clear(); }1.1 本质
MDC本质上就是个 ThreadLocal.
从LogbackMDCAdapter的代码中我们可以看出来 , 这个类的核心数据结构是 :
final ThreadLocal
1.2 例子下面三块反映了如何使用MDC.
1 java代码
public class SimpleMDC { static public void main(String[] args) throws Exception { // You can put values in the MDC at any time. Before anything else // we put the first name MDC.put("first", "Dorothy"); Logger logger = LoggerFactory.getLogger(SimpleMDC.class); // We now put the last name MDC.put("last", "Parker"); // The most beautiful two words in the English language according // to Dorothy Parker: logger.info("Check enclosed."); logger.debug("The most beautiful two words in English."); MDC.put("first", "Richard"); MDC.put("last", "Nixon"); logger.info("I am not a crook."); logger.info("Attributed to the former US president. 17 Nov 1973."); } }
2 配置
%X{first} %X{last} - %m%n
3 结果
Dorothy Parker - Check enclosed. Dorothy Parker - The most beautiful two words in English. Richard Nixon - I am not a crook.二 Automating access to the MDC
在处理多客户端的时候,MDC很有用. 通过MDC在线程中传递数据, 也通过MDC将数据透传给 appender . 虽然没有MDC 我们在logger.xxx()时 多打印出自己想要的信息也是可以,但是:其一没有封装, 其二通过方法人肉传参其实很繁琐.
2.1 案例一有一个web应用, 我们希望在日志中记录客户端的ip host user等信息,一个简便的方法是在 servlet Filter 中实现,比如:
public class UserServletFilter implements Filter { private final String USER_KEY = "username"; public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean successfulRegistration = false; HttpServletRequest req = (HttpServletRequest) request; Principal principal = req.getUserPrincipal(); // Please note that we could have also used a cookie to // retrieve the user name if (principal != null) { String username = principal.getName(); successfulRegistration = registerUsername(username); } try { chain.doFilter(request, response); } finally { if (successfulRegistration) { MDC.remove(USER_KEY); } } } public void init(FilterConfig arg0) throws ServletException { } private boolean registerUsername(String username) { if (username != null && username.trim().length() > 0) { MDC.put(USER_KEY, username); return true; } return false; } }
然后我们将这个Filter 注册到servlet 容器中(通过web.xml)
2.2 切线程正由于MDC的本质是ThreadLocal,在多线程环境中使用时需要特别注意.
如果涉及到线程切换, 我们可以在 当前线程中 MDC.getCopyOfContextMap(), 然后在另一个线程中 第一句就执行 MDC.setCopyOfContextMap(). 本质上这把 ThreadLocal的内容跨线程传递了.
MDCInsertingServletFilter是一个Logback内置的记录HTTP信息的Filter, 我们只要:
1 在web.xml中配置:
MDCInsertingServletFilter ch.qos.logback.classic.helpers.MDCInsertingServletFilter MDCInsertingServletFilter /*
2 在 appender 配置
%X{req.remoteHost} %X{req.requestURI}%n%d - %m%n
这里的 req.remoteHost就是一个MDC key (内置)
1 ,2 两步完成即可使用该Filter .
三 参考- 参考文献
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)