为什么80%的码农都做不了架构师?>>>
1 背景介绍
MDC
为每个线程建立一个独立的存储空间,开发人员可以根据需要把信息存入其中。MDC
使用Map机制来存储信息,信息以key/value对的形式存储在Map中,类似于ThreadLocal的作用(MDC底层就是ThreadLocal实现)。
使用Servlet Filter在接收到请求时,生成UUID填充 MDC
。在log4j的配置中就可以使用 %X{key}
打印MDC中的内容,从而识别出同一次请求中的LOG。
2 使用MDC打印ID
2.1 MDC
在 log4j 1.x
中 MDC
的使用方式如下:
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
// 填充数据
MDC.put(Contents.REQUEST_ID, UUID.randomUUID().toString());
chain.doFilter(request, response);
} finally {
// 请求结束时清除数据,否则会造成内存泄露问题
MDC.remove(Contents.REQUEST_ID);
}
}
2.2 ThreadContext
在 log4j 2.x
中,使用 ThreadContext
代替了 MDC
和 NDC
,使用方式如下:
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
// 填充数据
ThreadContext.put(Contents.REQUEST_ID, UUID.randomUUID().toString());
chain.doFilter(request, response);
} finally {
// 请求结束时清除数据,否则会造成内存泄露问题
ThreadContext.remove(Contents.REQUEST_ID);
}
}
2.3 写日志
-
%X
:打印Map中的所有信息 -
%X{key}
:打印指定的信息 -
%x
:打印堆栈中的所有信息
打印日志的格式案例如下:
3 总结
微服务架构中的链路追踪主要是用于快速定位故障点,使用MDC方式来将每一笔交易产生的所有日志都添加上请求ID,从而只需要该ID即可将整个架构中的所有有关日志收集至一出进行分析定位具体问题。
拓展 — 接入点
在微服务架构中,我们都梦想着所有请求都从服务网关统一接入,一般也都是从网关的接入层进行统一生成交易ID。如果能保证所有的交易都有一个统一的入口,则可以忽略这条提示;反之,若不能保证百分百的统一接入,则需要框架来支持提供填充(如果没有交易ID,则自动生成ID进行填充)。这一特性主要是用于串联非同一入口进来的这一类流量。