轉自:https://blog.csdn.net/userwyh/article/details/52862216
一、MDC介紹
MDC(Mapped Diagnostic Context,映射調試上下文)是 log4j 和 logback 提供的一種方便在多線程條件下記錄日誌的功能。某些應用程序採用多線程的方式來處理多個用戶的請求。在一個用戶的使用過程中,可能有多個不同的線程來進行處理。典型的例子是 Web 應用服務器。當用戶訪問某個頁面時,應用服務器可能會創建一個新的線程來處理該請求,也可能從線程池中複用已有的線程。在一個用戶的會話存續期間,可能有多個線程處理過該用戶的請求。這使得比較難以區分不同用戶所對應的日誌。當需要追蹤某個用戶在系統中的相關日誌記錄時,就會變得很麻煩。
一種解決的辦法是採用自定義的日誌格式,把用戶的信息採用某種方式編碼在日誌記錄中。這種方式的問題在於要求在每個使用日誌記錄器的類中,都可以訪問到用戶相關的信息。這樣纔可能在記錄日誌時使用。這樣的條件通常是比較難以滿足的。MDC 的作用是解決這個問題。
MDC 可以看成是一個與當前線程綁定的哈希表,可以往其中添加鍵值對。MDC 中包含的內容可以被同一線程中執行的代碼所訪問。當前線程的子線程會繼承其父線程中的 MDC 的內容。當需要記錄日誌時,只需要從 MDC 中獲取所需的信息即可。MDC 的內容則由程序在適當的時候保存進去。對於一個 Web 應用來說,通常是在請求被處理的最開始保存這些數據。
二、MDC使用案例
相對比較大的項目來說,一般會有多個開發人員,如果每個開發人員憑自己的理解打印日誌,那麼當用戶反饋問題時,很難通過日誌去快速的定位到出錯原因,也會消耗更多的時間。所以針對這種問題,一般會定義好整個項目的日誌格式,如果是需要追蹤的日誌,開發人員調用統一的打印方法,在日誌配置文件裏面定義好相應的字段,通過MDC功能就能很好的解決問題。
比如我們可以事先把用戶的sessionId,登錄用戶的用戶名,訪問的城市id,當前訪問商戶id等信息定義成字段,線程開始時把值放入MDC裏面,後續在其他地方就能直接使用,無需再去設置了。
使用MDC來記錄日誌,一來可以規範多開發下日誌格式的一致性,二來可以爲後續使用ELK對日誌進行分析。
所需依賴
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
log4j.xml配置樣例,追蹤日誌自定義格式主要在name="trance"的layout裏面進行設置,我們使用%X{userName}來定義此處會打印MDC裏面key爲userName的value,如果所定義的字段在MDC不存在對應的key,那麼將不會打印,會留一個佔位符。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss.SSS} %-6p%c:%L %x - %m%n" />
</layout>
</appender>
<appender name="error" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="D://logs//error.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="threshold" value="error"/>
<param name="append" value="true"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-6p%c:%L - %m%n" />
</layout>
</appender>
<appender name="logic" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="D://logs//logic.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="threshold" value="info"/>
<param name="append" value="true"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-6p%c:%L - %m%n" />
</layout>
</appender>
<appender name="trace" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="D://logs//trace.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="threshold" value="info"/>
<param name="append" value="true"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] - %X{mchId} - %X{mchName} - %X{siteName} - %X{sessionId} - %X{cityId} - %X{userName} - %X{mobile} - %m%n" />
</layout>
</appender>
<logger name="traceLog" additivity="false">
<level value="info" />
<appender-ref ref="trace" />
</logger>
<root>
<level value="info" />
<appender-ref ref="console"/>
<appender-ref ref="logic" />
<appender-ref ref="error" />
</root>
</log4j:configuration>
日誌打印類
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TraceLogger {
//此處的"tranceLog"爲log4j中定義的對應的 logger的name
private static final Logger TRACE_LOGGER = LoggerFactory.getLogger("traceLog");
private TraceLogger() {
}
public static void info(String message){
TRACE_LOGGER.info(message);
}
public static void info(String format,Object... arguments){
TRACE_LOGGER.info(format, arguments);
}
}
最後寫個日誌打印測試一下效果
@Test
public void Test(){
MDC.clear();
MDC.put("sessionId" , "f9e287fad9e84cff8b2c2f2ed92adbe6");
MDC.put("cityId" , 1);
MDC.put("siteName" , "北京");
MDC.put("userName" , "userwyh");
TraceLogger. info("測試MDC打印一");
MDC.put("mobile" , "110");
TraceLogger. info("測試MDC打印二");
MDC.put("mchId" , 12);
MDC.put("mchName", "商戶名稱");
TraceLogger. info("測試MDC打印三");
}
執行完後我們可以在定義的日誌輸出路徑下看到以下輸出
[2016-10-19 19:20:26.564] - - - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - - 測試MDC打印一
[2016-10-19 19:20:26.565] - - - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - 110 - 測試MDC打印二
[2016-10-19 19:20:26.565] - 12 - 商戶名稱 - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - 110 - 測試MDC打印三