在我们的软件中,我们广泛使用 MDC 强 >来跟踪诸如会话 ID 和 Web 请求的用户名之类的东西。这种方法在原始线程中运行时效果很好。
但是,有很多事情需要在后台处理。为此,我们使用 java.concurrent.ThreadPoolExecutor
和 java.util.Timer
类以及一些自滚动的 异步执行服务。所有这些服务都管理自己的线程池。
这就是 日志回放手册对在这样的环境中使用 MDC 所要说的:
映射的诊断上下文的副本不能始终由工作线程从起始线程继承。这就是 java.util.while 的情况。执行器用于线程管理。例如,newCachedThreadPool 方法创建一个 ThreadPoolExecator,并且像其他线程池代码一样,它具有复杂的线程创建逻辑。
在这种情况下,建议在向执行者提交任务之前,在原始(主)线程上调用 MDC.getCopyOfContextMap ()。当任务运行时,作为第一个操作,它应该调用 MDC.setContextMapValue ()来将原始 MDC 值的存储副本与新的 Execator 托管线程关联。
这很好,但是很容易忘记添加这些调用,并且在为时已晚之前没有简单的方法来识别问题。使用 Log4j 的唯一标志是您在日志中丢失了 MDC 信息,而使用 Logback 您将获得过时的 MDC 信息(因为胎面池中的线程从在它上面运行的第一个任务继承了它的 MDC)。这两者都是生产系统中的严重问题。
我没有看到我们的情况在任何方面特殊,但我无法找到很多关于这个问题在网上。显然,这不是很多人都会遇到的问题,所以一定有办法避免。我们到底做错了什么?