java获取MDC中的日志_跟我学Springboot开发后端管理系统8:AOP+logback+MDC日志输出

MDC介绍

在比较复杂的应用中,一个请求需要走很多个方法的处理,怎么样才能快速查找一个请求的全部日志呢。在分布式系统中,我们可以用链路追踪,比如zipkin、skywalking去快速查找日志,从而定位问题。在比较复杂的单体管理系统中,我们可以使用slf4j的MDC去实现类似的功能。

MDC ( Mapped Diagnostic Contexts ),是为了便于我们诊断线上问题而出现的方法工具类。使用ThreadLocal实现的,在MDC中的变量,每个线程都会有单独的副本,多线程不会相互干扰。MDC功能,logback 和 log4j 提供了支持。在Matrix-Web中,使用logback和slf4j进行日志的答应。

MDC原理

MDC类是一个静态工具类,对外提供了类似Map的接口:

public class MDC {

// 清空 map 所有的条目。

public static void clear();

// 根据 key 值返回相应的对象

public static object get(String key);

// 返回所有的 key 值 .

public static Enumeration getKeys();

// 把 key 值和关联的对象,插入 map 中

public static void put(String key, Object val),

// 删除 key 对应的对象

public static remove(String key)

}

为了弄清楚MDC的原理,我们来跟下MDC的源码,比如put方法,最终交给mdcAdapter去处理。

public static void put(String key, String val) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key parameter cannot be null");

}

if (mdcAdapter == null) {

throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);

}

mdcAdapter.put(key, val);

}

跟踪代码mdcAdapter是由StaticMDCBinder初始化出现的,即LogbackMDCAdapter的实例。

public class StaticMDCBinder {

/**

* The unique instance of this class.

*/

public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();

private StaticMDCBinder() {

}

/**

* Currently this method always returns an instance of

* {@link StaticMDCBinder}.

*/

public MDCAdapter getMDCA() {

return new LogbackMDCAdapter();

}

LogbackMDCAdapter中put方法最终是由copyOnThreadLocal去处理的。

public void put(String key, String val) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key cannot be null");

}

Map oldMap = copyOnThreadLocal.get();

Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp) || oldMap == null) {

Map newMap = duplicateAndInsertNewMap(oldMap);

newMap.put(key, val);

} else {

oldMap.put(key, val);

}

}

而copyOnThreadLocal是一个ThreadLocal。

final ThreadLocal> copyOnThreadLocal = new ThreadLocal>();

由此可见MDC最终是由ThreadLocal去存放和取key、value的。

在Matrix-Web中使用MDC

在Matrix-web中使用Filter去做MDC的处理,在请求进入业务请求逻辑之前,将前端生成的REQUEST_ID存储在MDC中。当请求的业务逻辑完成后,将MDC清除。

public class LogFilter implements Filter {

public static final String REQUEST_ID="REQUEST_ID";

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

//REQUEST_ID由前端生成,

MDC.put(REQUEST_ID, getRequestId(httpServletRequest));

filterChain.doFilter(servletRequest, servletResponse);

MDC.clear();

}

}

}

在logback.xml中配置打印REQUEST_ID,用这个配置%X{REQUEST_ID}。具体配置如下:

%d{yyyy-MM-dd HH:mm:ss.SSS} %X{REQUEST_ID} [%thread] %-5level %logger{50} - %msg%n

${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.log

30

%d{yyyy-MM-dd HH:mm:ss.SSS} %X{REQUEST_ID} [%thread] %-5level %logger{50} - %msg%n

10MB

定义一个RespDTO,该类用于Controller统一返回结果,在该类中,会自动requestId赋值给这个类,统一给前端,这样前端页面也能够从请求结果上查到requestId。

public class RespDTO implements Serializable {

public int code = 0;

public String message = "";

public T data;

public String requestId;

public static RespDTO onSuc(Object data) {

RespDTO resp = new RespDTO();

String requestId = MDC.get(REQUEST_ID);

if (!StringUtils.isEmpty(requestId)) {

resp.requestId = requestId;

}

resp.message="sucess";

resp.data = data;

return resp;

}

@Override

public String toString() {

return "RespDTO{" +

"code=" + code +

", error='" + message + '\'' +

", data=" + data +

'}';

}

}

使用MDC能够将一个请求的所有业务处理逻辑的日志通过一个唯一的标识串起来,方便日志的排查。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值