1. 程式人生 > >MDCLog4j+ELK完美日誌方案

MDCLog4j+ELK完美日誌方案

LoggerMDCFilter設定MDC引數

@WebFilter(filterName="log",urlPatterns={"/*"})
public class LoggerMDCFilter extends OncePerRequestFilter implements Filter{

    protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws ServletException,IOException {
        try 
{ TraceUtils.beginTrace(); MDC.put("req.requestURI", StringUtils.defaultString(request.getRequestURI())); MDC.put("req.queryString", StringUtils.defaultString(request.getQueryString())); MDC.put("req.requestURIWithQueryString", request.getRequestURI() + (request.getQueryString() == null ? "" : "?"
+request.getQueryString())); MDC.put("req.remoteAddr", StringUtils.defaultString(request.getRemoteAddr())); //為每一個請求建立一個ID,方便查詢日誌時可以根據ID查找出一個http請求所有相關日誌 MDC.put("req.id", StringUtils.remove(UUID.randomUUID().toString(),"-")); chain.doFilter(request, response); }finally { clearMDC(); } } private void
clearMDC() { TraceUtils.endTrace(); } }

appender配置

<!-- 控制檯輸出 -->   
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> 
<!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符--> 
        <!--<pattern>%X{traceId} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>-->
<pattern>%X{traceId} %X{req.remoteAddr}/%X{req.id} - %X{req.requestURI}?%X{req.queryString}] %-5p %c{2} - %m%n</pattern>
    </encoder>
</appender>

看一下MDClog4j效果

NDC和MDC

NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j種非常有用的兩個類,它們用於儲存應用程式的上下文資訊(context infomation),從而便於在log中使用這些上下文資訊。

NDC採用了一個類似棧的機制來push和pop上下文資訊,每一個執行緒都獨立地儲存上下文資訊。比如說一個servlet就可以針對每一個request建立對應的NDC,儲存客戶端地址等等資訊。

當使用的時候,我們要儘可能確保在進入一個context的時候,把相關的資訊使用NDC.push(message);在離開這個context的時候使用NDC.pop()將資訊刪除。另外由於設計上的一些問題,還需要保證在當前thread結束的時候使用NDC.remove()清除記憶體,否則會產生記憶體洩漏的問題。

儲存了上下文資訊之後,我們就可以在log的時候將資訊輸出。在相應的PatternLayout中使用”%x”來輸出儲存的上下文資訊,下面是一個PatternLayout的例子:

%r [%t] %-5p %c{2} %x - %m%n

使用NDC最重要的好處就是,當我們想輸出一些上下文的資訊的時候,不需要讓logger去尋找這些資訊,而只需要在適當的位置進行儲存,然後再配置檔案中修改PatternLayout。在最新的log4j 1.3版本中增加了一個org.apache.log4j.filters.NDCMatchFilter,用來

根據NDC中儲存的資訊接受或拒絕一條log資訊。

MDC和NDC非常相似,所不同的是MDC內部使用了類似map的機制來儲存資訊,上下文資訊也是每個執行緒獨立地儲存,所不同的是資訊都是以它們的key值儲存在”map”中。相對應的方法,MDC.put(key, value); MDC.remove(key); MDC.get(key); 在配置PatternLayout的時候使用:%x{key}來輸出對應的value。同樣地,MDC也有一個org.apache.log4j.filters.MDCMatchFilter。這裡需要注意的一點,MDC是執行緒獨立的,但是一個子執行緒會自動獲得一個父執行緒MDC的copy。

至於選擇NDC還是MDC要看需要儲存的上下文資訊是堆疊式的還是key/value形式的。

動態修改日誌配置

在開發過程中,我們經常會遇到修改log4j配置的情況,在這種情況下,頻繁重啟應用顯然是不可接受的。幸好log4j提供了自動重新載入配置檔案的能力,在配置檔案修改後,便會自己重新載入配置。在1.2及以前的版本中DOMConfigurator和PropertyConfigurator都提供了configureAndWatch方法,對指定的配置檔案進行監控,並且可以設定檢查的間隔時間。

在開發與生產環境中,我們有時候需要對日誌的配置進行動態切換,要除錯、監控和檢查系統的執行時資訊。

  一般有兩種方法 

  1、通過 Log4j的 Log4jConfigListener 在啟動時開啟定時器進行定時載入配置檔案 
  2、通過 JMX 動態控制 

  簡單介紹一下上面兩種方法: 

  1、通過 Log4j的 Log4jConfigListener,實現方法比較簡單,原理是需要在後臺開啟一個監聽執行緒,定時掃描,然後來定時工作.

             實現方法如下:

  1. /**  
  2.     * 裝載log4j配置檔案  
  3.     */
  4.    publicstaticvoid load() {    
  5.        String path = Log4jConfig.class.getClass().getResource("/").getPath()+ "log4j.properties";    
  6.        System.out.println("log4j configfile path=" + path);    
  7.        // 間隔特定時間,檢測檔案是否修改,自動重新讀取配置   
  8.        PropertyConfigurator.configureAndWatch(path,1000);  
  9.    }  


  2、通過 JMX 動態控制的則必須供一個管理的埠,不僅有可能埠被佔用(當然有個 workaround 來解決它),還有存在防火牆等等需要配置這個管理埠進行對外暴露等等。 

  雖然上述兩種方法存在著一些不足,但是這兩種方法在特定的場合下,都可以很好地來利用它進行完美地工作。 

  現在,利用它進行封裝與擴充套件,我們可以巧妙地進行定製,並通過 Web Console 介面來更方便地進行動態切換配置資訊,而且不需要重新啟動正在執行中的應用程式。 



海量視覺化日誌分析平臺之ELK搭建

1、elasticsearch-1.7.3

啟動:./bin/elasticsearch -d 埠:9200 9300 2、logstash-1.5.1-1 啟動:./bin/logstash -f conf/stash-log4j.conf
埠:4560 3、kibana-4.1.2 啟動:./kibana & 埠:5601