在web环境下进行日志记录,时刻需要跟踪用户,特别是多线程下进行日志记录可能会导致一些问题

LogBack 有个 MDC(Mapped Diagnostic Context,映射调试上下文) 是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。

MDC : https://logback.qos.ch/manual/mdc.html#autoMDC
http://www.cnblogs.com/dumuqiao/p/3654702.html?utm_source=tuicool&utm_medium=referral

  • 在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线程池中复用已有的线程。在一个用户的会话存续期间,可能有多个线程处理过该用户的请求。这使得比较难以区分不同用户所对应的日志。当需要追踪某个用户在系统中的相关日志记录时,就会变得很麻烦。

MDC内部就是使用 ThreadLocal



当然这里要介绍的是 Log4j2 的 ThreadContext,功能和MDC差不多,但是Log4j2的性能更好.

官方性能测试报告 : http://logging.apache.org/log4j/2.x/performance.html

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

import com.aidijing.common.LogUtils;
import com.aidijing.common.RequestUtils;
import org.apache.logging.log4j.ThreadContext;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class RequestLoggingFilter implements Filter {
///////////////////////////////////////////////////////////////////////////
// 在log4j2.xml配置文件通过 %X{xxx} 获取
///////////////////////////////////////////////////////////////////////////
/** 该次请求的请求信息 */
private static final String REQUEST_MESSAGE = "requestMessage";
/** 用户ID */
private static final String USER_ID = "userId";
/** 用户姓名 */
private static final String USER_NAME = "userName";

@Override
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException,
ServletException {
final BodyReaderWrapper wrapper = new BodyReaderWrapper( ( HttpServletRequest ) request );
final String username = "用户名A"; // 这里为了测试,实际环境中,自行修改
ThreadContext.put( USER_ID, wrapper.getRequestedSessionId() ); // 这里也是测试
ThreadContext.put( USER_NAME, username );
LogUtils.getLogger().info( RequestUtils.getRequestMessage( wrapper, USER_ID, username ) );
chain.doFilter( wrapper, response );
ThreadContext.clearAll();
}

@Override
public void init ( FilterConfig filterConfig ) throws ServletException {
}

@Override
public void destroy () {
}

}
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
<Configuration monitorInterval="3600" shutdownHook="disable">
<Appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<!--
输出日志的格式
%X{userId} : 从ThreadContext中获取,key为ThreadContext.put进去的key
%X{userName} : 同上
%X : 则打印所有
-->
<PatternLayout pattern="[%X{userId}] [%X{userName}] %d{yyyy-MM-dd HH:mm:ss SSS} [%p] [%t] %c.%M(%L) | %m%n"/>
</Console>
</Appenders>

<Loggers>

<logger name="com.aidijing" level="debug"/>
<logger name="org.mybatis" level="info" />
<logger name="org.springframework" level="info"/>
<logger name="com.alibaba.druid" level="info"/>

<Root level="debug">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

效果如下: