問題1:我們希望 當有一個請求時, 全部相應后台日志增加一個統一的全局requestId 方便日志搜集和處理:
我們可以使用sl4j MDC機制。
問題2:sl4j MDC機制原理:
通常用於保存線程本地的“診斷數據”然后有日志組件打印,其內部時基於threadLocal實現
問題3:MDC如何使用:
步驟 1 在攔截器 或者 統一的方法入口加上
MDC.put(“REQUEST_ID”,xxx);
步驟 2 改動log4j日志格式,增加該字段 REQUEST_ID 比如下面例子即可:
改前:%-d{yyyy-MM-dd HH:mm:ss,SSS} [%p] % %m%n 改后:%-d{yyyy-MM-dd HH:mm:ss,SSS} [%p] %X{REQUEST_ID} %m%n
問題4:但對於多線程情況,比如主線程創建了一些子線程,那么這些子線程能不能自動帶上MDC所賦的值呢?
答案是可能會出問題。
我們看官方文檔說明:https://logging.apache.org/log4j/2.x/manual/thread-context.html
=========================================================================================
Implementation details
The Stack and the Map are managed per thread and are based on ThreadLocal by default. The Map can be configured to use an InheritableThreadLocal by setting system property log4j2.isThreadContextMapInheritable to true. When configured this way, the contents of the Map will be passed to child threads. However, as discussed in the Executors class and in other cases where thread pooling is utilized, the ThreadContext may not always be automatically passed to worker threads. In those cases the pooling mechanism should provide a means for doing so. The getContext() and cloneStack() methods can be used to obtain copies of the Map and Stack respectively.
Note that all methods of the ThreadContext class are static.
翻譯過來 ==================================================================================
實施細節
堆棧和映射是按線程管理的,默認情況下基於ThreadLocal。 通過將系統屬性log4j2.isThreadContextMapInheritable設置為true,可以將Map配置為使用InheritableThreadLocal。 通過這種方式配置后,映射的內容將傳遞給子線程。 但是,如在Executors類中討論的以及在其他使用線程池的情況下,ThreadContext可能不會始終自動傳遞給工作線程。 在這些情況下,池化機制應提供這樣做的手段。 getContext()和cloneStack()方法可分別用於獲取Map和Stack的副本。
請注意,ThreadContext類的所有方法都是靜態的。
=========================================================================================
測試使用的是線程池,跑了一次 發現線程池中的子線程會把MDC的值打印出來,並且和主線程MDC值一致,與官方文檔矛盾,經過分析發現:
用線程池的時候會有問題的,測試ok,是因為工作線程第一次的時候是當前的線程創建的,如果復用的話會出現問題的,果然把線程池大小改成1,
然后多調幾次就發現子線程中的MDC值永遠是第一次創建時的值不會變,
所以確認官方文檔是對的:主線程中設置的MDC數據,在其子線程(多線程池)中是無法獲取的。
下面就來介紹如何解決這個問題。
在子線程類經過如上代碼改動,則主線程中的MDC值會成功的自動傳遞給該子線程,親測有效。