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 voidclearMDC() { 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,實現方法比較簡單,原理是需要在後臺開啟一個監聽執行緒,定時掃描,然後來定時工作.
實現方法如下:
- /**
- * 裝載log4j配置檔案
- */
- publicstaticvoid load() {
- String path = Log4jConfig.class.getClass().getResource("/").getPath()+ "log4j.properties";
- System.out.println("log4j configfile path=" + path);
- // 間隔特定時間,檢測檔案是否修改,自動重新讀取配置
- PropertyConfigurator.configureAndWatch(path,1000);
- }
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