log4j MDC用戶操作日誌追蹤配置

一、MDC介紹

        MDC(Mapped Diagnostic Context,映射調試上下文)是 log4j 和 logback 提供的一種方便在多線程條件下記錄日誌的功能。某些應用程序採用多線程的方式來處理多個用戶的請求。在一個用戶的使用過程中,可能有多個不同的線程來進行處理。典型的例子是 Web 應用服務器。當用戶訪問某個頁面時,應用服務器可能會創建一個新的線程來處理該請求,也可能從線程池中複用已有的線程。在一個用戶的會話存續期間,可能有多個線程處理過該用戶的請求。這使得比較難以區分不同用戶所對應的日誌。當需要追蹤某個用戶在系統中的相關日誌記錄時,就會變得很麻煩。

  一種解決的辦法是採用自定義的日誌格式,把用戶的信息採用某種方式編碼在日誌記錄中。這種方式的問題在於要求在每個使用日誌記錄器的類中,都可以訪問到用戶相關的信息。這樣纔可能在記錄日誌時使用。這樣的條件通常是比較難以滿足的。MDC 的作用是解決這個問題。

  MDC 可以看成是一個與當前線程綁定的哈希表,可以往其中添加鍵值對。MDC 中包含的內容可以被同一線程中執行的代碼所訪問。當前線程的子線程會繼承其父線程中的 MDC 的內容。當需要記錄日誌時,只需要從 MDC 中獲取所需的信息即可。MDC 的內容則由程序在適當的時候保存進去。對於一個 Web 應用來說,通常是在請求被處理的最開始保存這些數據。

二、MDC使用案例

     相對比較大的項目來說,一般會有多個開發人員,如果每個開發人員憑自己的理解打印日誌,那麼當用戶反饋問題時,很難通過日誌去快速的定位到出錯原因,也會消耗更多的時間。所以針對這種問題,一般會定義好整個項目的日誌格式,如果是需要追蹤的日誌,開發人員調用統一的打印方法,在日誌配置文件裏面定義好相應的字段,通過MDC功能就能很好的解決問題。

     比如我們可以事先把用戶的sessionId,登錄用戶的用戶名,訪問的城市id,當前訪問商戶id等信息定義成字段,線程開始時把值放入MDC裏面,後續在其他地方就能直接使用,無需再去設置了。

    使用MDC來記錄日誌,一來可以規範多開發下日誌格式的一致性,二來可以爲後續使用ELK對日誌進行分析。

    所需依賴 

[plain] view plain copy
  1. <dependency>  
  2.   <groupId>log4j</groupId>  
  3.   <artifactId>log4j</artifactId>  
  4.   <version>1.2.17</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>org.slf4j</groupId>  
  8.     <artifactId>slf4j-log4j12</artifactId>  
  9.     <version>1.7.21</version>  
  10. </dependency>   

log4j.xml配置樣例,追蹤日誌自定義格式主要在name="trance"的layout裏面進行設置,我們使用%X{userName}來定義此處會打印MDC裏面key爲userName的value,如果所定義的字段在MDC不存在對應的key,那麼將不會打印,會留一個佔位符。


[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">  
  3. <log4j:configuration>  
  4.     <appender name="console" class="org.apache.log4j.ConsoleAppender">  
  5.         <param name="target" value="System.out" />  
  6.         <layout class="org.apache.log4j.PatternLayout">  
  7.             <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss.SSS} %-6p%c:%L %x - %m%n" />  
  8.         </layout>  
  9.     </appender>  
  10.   
  11.     <appender name="error" class="org.apache.log4j.DailyRollingFileAppender">  
  12.         <param name="File" value="D://logs//error.log" />  
  13.         <param name="DatePattern" value="'.'yyyy-MM-dd" />  
  14.         <param name="threshold" value="error"/>  
  15.         <param name="append" value="true"/>  
  16.         <layout class="org.apache.log4j.PatternLayout">  
  17.             <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-6p%c:%L - %m%n" />  
  18.         </layout>  
  19.     </appender>  
  20.   
  21.     <appender name="logic" class="org.apache.log4j.DailyRollingFileAppender">  
  22.         <param name="File" value="D://logs//logic.log" />  
  23.         <param name="DatePattern" value="'.'yyyy-MM-dd" />  
  24.         <param name="threshold" value="info"/>  
  25.         <param name="append" value="true"/>  
  26.         <layout class="org.apache.log4j.PatternLayout">  
  27.             <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-6p%c:%L - %m%n" />  
  28.         </layout>  
  29.     </appender>  
  30.   
  31.     <appender name="trace" class="org.apache.log4j.DailyRollingFileAppender">  
  32.         <param name="File" value="D://logs//trace.log" />  
  33.         <param name="DatePattern" value="'.'yyyy-MM-dd" />  
  34.         <param name="threshold" value="info"/>  
  35.         <param name="append" value="true"/>  
  36.         <layout class="org.apache.log4j.PatternLayout">  
  37.                <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" />  
  38.          </layout>  
  39.     </appender>  
  40.   
  41.     <logger name="traceLog" additivity="false">  
  42.         <level value="info" />  
  43.         <appender-ref ref="trace" />  
  44.     </logger>  
  45.   
  46.     <root>  
  47.         <level value="info" />  
  48.         <appender-ref ref="console"/>  
  49.         <appender-ref ref="logic" />  
  50.         <appender-ref ref="error" />  
  51.     </root>  
  52. </log4j:configuration>  

日誌打印類

[java] view plain copy
  1. import org.slf4j.Logger;  
  2. import org.slf4j.LoggerFactory;  
  3.   
  4. public class TraceLogger {  
  5.     //此處的"tranceLog"爲log4j中定義的對應的 logger的name  
  6.     private static final Logger TRACE_LOGGER = LoggerFactory.getLogger("traceLog");  
  7.   
  8.     private TraceLogger() {  
  9.           
  10.     }  
  11.     
  12.     public static void info(String message){  
  13.         TRACE_LOGGER.info(message);  
  14.     }  
  15.   
  16.     public static void info(String format,Object... arguments){  
  17.         TRACE_LOGGER.info(format, arguments);  
  18.     }  
  19. }  

最後寫個日誌打印測試一下效果

[java] view plain copy
  1. @Test  
  2. public void Test(){  
  3.     MDC.clear();  
  4.     MDC.put("sessionId" , "f9e287fad9e84cff8b2c2f2ed92adbe6");  
  5.     MDC.put("cityId" , 1);  
  6.     MDC.put("siteName" , "北京");  
  7.     MDC.put("userName" , "userwyh");  
  8.     TraceLogger. info("測試MDC打印一");  
  9.            
  10.     MDC.put("mobile" , "110");  
  11.     TraceLogger. info("測試MDC打印二");  
  12.            
  13.     MDC.put("mchId" , 12);  
  14.     MDC.put("mchName""商戶名稱");  
  15.     TraceLogger. info("測試MDC打印三");  
  16.            
  17. }  

執行完後我們可以在定義的日誌輸出路徑下看到以下輸出

[java] view plain copy
  1. [2016-10-19 19:20:26.564] -  -  - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh -  - 測試MDC打印一  
  2. [2016-10-19 19:20:26.565] -  -  - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - 110 - 測試MDC打印二  
  3. [2016-10-19 19:20:26.565] - 12 - 商戶名稱 - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - 110 - 測試MDC打印三  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章