# 链路追踪
## ThreadContext
`NDC`(Nested Diagnostic Context)和`MDC`(Mapped Diagnostic Context)是`log4j`种非常有用的两个类,它们用于存储应用程序的上下文信息(Context Infomation),从而便于在`log`中使用这些上下文信息。`NDC`采用了一个类似栈的机制来push和pop上下文信息,每一个线程都独立地储存上下文信息。比如说一个servlet就可以针对每一个request创建对应的NDC,储存客户端地址等等信息。`MDC`和`NDC`非常相似,所不同的是`MDC`内部使用了类似map的机制来存储信息,上下文信息也是每个线程独立地储存,所不同的是信息都是以它们的key值存储在”map”中。
NDC和MDC的原理是用了java的ThreadLocal类。可以针对不同线程存储信息。但是今天在log4j2上使用时发现没有找到NDC和MDC。查找官方文档,原来是换成了ThreadContext。
微服务架构中的链路追踪主要是用于快速定位故障点,使用MDC方式来将每一笔交易产生的所有日志都添加上请求ID,从而只需要该ID即可将整个架构中的所有有关日志收集至一出进行分析定位具体问题。
### NDC
NDC采用栈的机制存储上下文,线程独立的,子线程会从父线程拷贝上下文。其调用方法以下:
- 开始调用
`NDC.push(message);`
- 删除栈顶消息
`NDC.pop();`
- 清除所有的消息,必须在线程退出前显示的调用,不然会致使内存溢出。
`NDC.remove();`
- 输出模板,注意是小写的 `[%x]`
`log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ssS}] [%x] : %m%n`
### MDC
MDC采用Map的方式存储上下文,线程独立的,子线程会从父线程拷贝上下文。其调用方法以下:
- 保存信息到上下文
`MDC.put(key, value);`
- 从上下文获取设置的信息
`MDC.get(key);`
- 清楚上下文中指定的key的信息
`MDC.remove(key);`
- 清除全部
`clear();`
- 输出模板,注意是大写 `[%X{key}]`
`log4j.appender.consoleAppender.layout.ConversionPattern = %-4r [%t] %5p %c %x - %m - %X{key}%n`
在 `log4j 1.x` 中 `MDC` 的使用方式如下:
```java
@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);
}
}
```
### ThreadContext
在 `log4j 2.x` 中,使用 `ThreadContext` 代替了 `MDC` 和 `NDC`,使用方式如下:
```java
@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);
}
}
```
### 写日志
- `%X` :打印Map中的所有信息
- `%X{key}` :打印指定的信息
- `%x` :打印堆栈中的所有信息
打印日志的格式案例如下:
```xml
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %X{REQUEST_ID} %logger{36} - %msg%n" />
```