文章目录
- 背景
- MDC简介
背景
在定位问题时,我们希望通过grep traceId=xxx *.log
快速的过滤出某次请求的所有日志。
MDC简介
SLF4J
日志框架提供了一个MDC(Mapped Diagnostic Contexts)工具类
,
MDC可以帮我们记录追踪日志的功能
,它支持 Log4J
和LogBack
两种日志框架,通常打印出的日志会有线程号等信息来标志当前日志属于哪个线程。
使用日志控件提供的MDC功能,生成一个唯一序列标记一个线程的日志。
MDC原理简介
MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。
MDC 中包含的内容可以被同一线程中执行的代码所访问。
当需要记录日志时,只需要从 MDC 中获取所需的信息即可。 配置好日志格式即可打印带有traceId的日志如:
2024-04-04 20:37:05.572 [main] [traceId=dab8412a-f070-4406-9940-89fcdbd3473f] DEBUG com.example.mdc.mdc.Main - log in main thread 1
2024-04-04 20:37:05.572 [main] [traceId=dab8412a-f070-4406-9940-89fcdbd3473f] DEBUG com.example.mdc.mdc.Main - log in main thread 2
2024-04-04 20:37:05.572 [main] [traceId=dab8412a-f070-4406-9940-89fcdbd3473f] DEBUG com.example.mdc.mdc.Main - log in main thread 3
MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
服务之间的调用,则需要上层服务在 header 中添加标识同请求一起传输过来。下层服务直接使用上层服务的标识,就可以将日志串联起来。
有了MDC工具,只要在过滤器/拦截器/接口切面植入MDC.put("traceId", traceId)
和MDC.remove("traceId")
代码。
新增一个过滤器,检查请求的请求头是否有traceId信息,有直接用,没有就生成一个traceId。
Logback日志
LogbackMDCAdapter implements MDCAdapter
数据读写都是基于ThreadLocal。
MDC的局限性
● 父子线程数据无法传递
● 线程池使用MDC存在数据传递重复
阿里巴巴开源的transmittable-thread-local解决了以上2个问题,
其解决思路:
1、针对父子线程数据无法传递问题,TransmittableThreadLocal继承并加强InheritableThreadLocal类;2、针对线程池InheritableThreadLocal数据数据传递存在重复问题,TransmittableThreadLocal提供了TtlRunnable和TtlCallable来修饰提交到线程池的任务,保证每次任务执行前强制从父线程copy下ThreadLocalMap的最新的值。
使用TtlMDCAdapter,整合TransmittableThreadLocal的能力并直接实现MDCAdapter接口