解决痛点
在分布式系统中,无关日志穿行其中,导致我们无法查看整个调用过程;我们可能需要对一个用户的操作进行归类,例如:线程ID+时间戳,或者requestId等。如此我们可以从大量日志中grep中某个用户的操作流程
slf4j与MDC介绍
MDC ( Mapped Diagnostic Contexts ),顾名思义,其目的是为了便于我们诊断线上问题而出现的方法工具类。虽然,Slf4j 是用来适配其他的日志具体实现包的,但是针对 MDC功能,目前只有logback 以及 log4j 支持,或者说由于该功能的重要性,slf4j 专门为logback系列包装接口提供外部调用
主要用到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();
}
一般我们在代码中只需要调用put方法,将对应的值put到线程上下文Map中,然后在使用的地方调用get方法即可使用;对一些线程池的使用场景,我们可能需要清理数据等操作,需要调用clear方法
MDC最简使用示例
public class LogTest {
private static final Logger logger = LoggerFactory.getLogger(LogTest.class);
public static void main(String[] args) {
MDC.put("THREAD_ID", String.valueOf(Thread.currentThread().getId()));
logger.info("纯字符串信息的info级别日志");
}
}
logback.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.base" value="${catalina.base}/logs" />
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>[%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5p) %logger.%M\(%F:%L\)] %X{THREAD_ID} %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="console" />
</root>
</configuration>
于是就输出:
[2018-04-22 15:34:35 INFO com.zbj.log4j.LogTest.main(LogTest.java:29)] 1 纯字符串信息的info级别日志
在logback.xml中使用%{ }来占位,替换到MDC中对应的MDC的key值
普通Web项目中使用
直接使用Intercept、Filter或者Aspect三者之一即可
ROP框架中使用
需要将新增的拦截器加入到ROP拦截器中,如下所示
<!--注册拦截器,可配置多个-->
<rop:interceptors>
<bean class="com.zbj.**.**Interceptor"/>
</rop:interceptors>
即在拦截器中的
public class Open**Interceptor extends AbstractInterceptor {
/**
* 会设置到logback中 requestId 的值
*/
private final String SID = "requestId";
/**
* 在数据绑定后,服务方法调用前执行该拦截方法
*
* @param ropRequest
*/
@Override
public void beforeService(RopRequest ropRequest) {
RopRequestContext ropRequestContext = ropRequest.getRopRequestContext();
MDC.put(SID, ropRequestContext.getRequestId());
}
/**
* 在数据绑定后,服务方法调用前执行该拦截方法
*
* @param ropRequestContext
*/
@Override
public void beforeService(RopRequestContext ropRequestContext) {
}
/**
* 在服务执行完成后,响应返回前执行该拦截方法
*
* @param ropRequestContext
*/
@Override
public void beforeResponse(RopRequestContext ropRequestContext) {
MDC.remove(SID);
}
/**
* 对method为user.add的方法进行拦截,你可以通过methodContext中的信息制定拦截方案
*
* @param ropRequestContext
* @return
*/
@Override
public boolean isMatch(RopRequestContext ropRequestContext) {
return false;
}
}
参考文章:
https://blog.csdn.net/liubo2012/article/details/46337063
http://shift-alt-ctrl.iteye.com/blog/2345272
https://blog.csdn.net/sunzhenhua0608/article/details/29175283