MDC,映射调试上下文,跟着线程走的日志信息容器。
题图:from Google
引子
MDC(Mapped Diagnostic Context,映射调试上下文)是Logback提供的一种方便在多线程条件下记录日志的功能。使用MDC可以很方便的针对不同线程记录对应的日志信息。
一、MDC简介
Logback的设计目标之一就是为了帮助人们对复杂系统进行审计和调试,而实际运行的系统很多都会处理多线程。以web系统为例,每个请求都是一个单独的线程,如果需要针对不同线程记录对应的日志信息,应该怎么解决?
或许,我们可以为每一个线程配置不同的logger。但是这将导致logger数量大大增加,并且最终无法控制。
那么,有没有一种轻量级的、简单方便的解决方案呢?
有!那就是MDC。
如果把每个线程比作路上的行人,那么MDC就是每个人身上的背包,用来保存其专属的信息。
二、MDC的使用
MDC类只有静态方法,开发者可以把信息放进一个MDC,之后用其他logback组件获取这些信息。MDC是基于每个线程进行管理的。子线程自动继承其父的MDC的一个副本。换言之,MDC是线程安全的。比如,当一个web应用接收到一个请求时,开发者可以向MDC里插入恰当的信息,比如用户ip、请求参数等等。Logback组件会自动在每个记录条目里包含这些信息。
对于Servlet实现的web应用而言,针对每个请求进行特殊处理,采用filter的方式无疑最简单,以下就以一个简单filter演示MDC的使用。进行以下配置后,这次请求之内的日志输出都会带有MDC中存储的请求参数。
filter代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class LogFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { MDC.clear(); MDC.put("utm_content", request.getParameter("utm_content")); MDC.put("utm_term", request.getParameter("utm_term")); MDC.put("utm_campaign", request.getParameter("utm_campaign")); MDC.put("utm_media", request.getParameter("utm_media")); MDC.put("utm_source", request.getParameter("utm_source")); chain.doFilter(request, response); } @Override public void destroy() { } }
|
logback配置(%mdc是将MDC中所有信息输出,也可以使用%mdc{utm_media,utm_source}方式指定输出内容)
1 2 3 4 5 6 7 8 9 10 11
| <configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level - %mdc%n</Pattern> </layout> </appender> <root value="debug"> <appender-ref ref="CONSOLE" /> </root> </configuration>
|
三、MDC原理
在享受MDC带来的便利时,有没有想过它的实现原理是什么?
其实只要熟悉多线程使用,大家很容易想到一个词,ThreadLocal!没错,其实MDC就是一个ThreadLocal的HashMap。
具体实现并不复杂,大家可以跟踪一下MDC的源码即可,它的本体其实就是LogbackMDCAdapter这个类。