需求:html
目前的程序中都是基於log4j來實現日誌的管理,想要獲取日誌中的一部分消息,展現給用戶。java
約束:apache
因爲程序中除了本身開發的代碼,還會有層層依賴的第三方jar中的日誌輸出。須要展現給用戶的消息,也有部分包括在第三方的包中。不可能去修改第三方jar來得到消息,因此只能從Log4j自己的消息入手,獲取log4j的消息來進行處理。api
方案:session
第一步,增長新的loggerapp
1 log4j.rootLogger=INFO, console 2 3 log4j.appender.console=org.apache.log4j.ConsoleAppender 4 log4j.appender.console.layout=org.apache.log4j.PatternLayout 5 log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-20c %X{key} %x - %m%n 6 7 log4j.logger.hermes=INFO, hermes 8 log4j.appender.hermes=org.apache.log4j.ConsoleAppender 9 log4j.appender.hermes.layout=org.apache.log4j.PatternLayout 10 log4j.appender.hermes.layout.ConversionPattern=%X{key} %x %m%n 11 12 log4j.logger.org.apache.flink.yarn=INFO, hermes
如上,第1行定義了一個全局的logger,這個沒有疑義,全部的日誌都會輸出到這個logger。ide
關鍵是第7行,又定義了一個叫hermes的logger,它有一個同名的appender叫hermes,簡化了他的layout,只是拿出必須的輸出日誌便可。注意這裏有一個%X{key}的配置項,這裏是爲了拿到MDC的值,好區分這條消息是哪一個session打印出來的。函數
第12行也很關鍵,將包org.apache.flink.yarn的日誌都按照hermes是標準輸出,這裏是包名,固然也能夠指定到具體的類名。ui
如此這樣,對於包org.apache.flink.yarn中的日誌,會打印兩份,一份是以console的形式輸出,一份是以hermes的形式輸出。this
固然,咱們不但願在日誌中見到兩行實質上同樣的內容,這就是第二步要作到事情。
第二步:截取消息
這裏有兩個方案:一個方案是自定義appender,獲取消息;一個方案是獲取指定的logger,獲取消息。
方案1:自定義appender
1 import org.apache.log4j.AppenderSkeleton; 2 import org.apache.log4j.LogManager; 3 import org.apache.log4j.spi.LoggingEvent; 4 import org.slf4j.Logger; 5 import org.slf4j.LoggerFactory; 6 7 public class AdapterAppender extends AppenderSkeleton { 8 @Override 9 protected void append(LoggingEvent loggingEvent) { 10 String message = this.layout.format(loggingEvent); 11 System.out.println("<<<<<<<<<<***********"+message+"***********>>>>>>>>>>"); 12 } 13 14 @Override 15 public void close() { 16 17 } 18 19 @Override 20 public boolean requiresLayout() { 21 return true; 22 } 23 }
自定義appender仍是很簡單的,只要繼承AppenderSkeleton便可。其中的append方法就能夠拿到日誌消息,標準化以後就能夠拿來用了。
固然,上面log4j的配置也就須要改下了:
log4j.appender.hermes=cn.123.flink.log.AdapterAppender
方案2:獲取logger來獲取消息
1 import java.io.IOException; 2 import java.io.PipedReader; 3 import java.io.PipedWriter; 4 import java.io.Writer; 5 import java.util.Scanner; 6 7 import org.apache.log4j.*; 8 9 public class LogAppender extends Thread{ 10 11 private PipedReader reader; 12 13 public LogAppender(String appenderName) { 14 try { 15 Logger root = Logger.getLogger(appenderName); 16 // 獲取子記錄器的輸出源 17 Appender appender = root.getAppender(appenderName); 18 // 定義一個未鏈接的輸入流管道 19 reader = new PipedReader(); 20 // 定義一個已鏈接的輸出流管理,並鏈接到reader 21 Writer writer = new PipedWriter(reader); 22 // 設置 appender 輸出流 23 ((WriterAppender) appender).setWriter(writer); 24 }catch (IOException ioe){ 25 ioe.printStackTrace(); 26 } 27 } 28 29 @Override 30 public void run() { 31 // 不間斷地掃描輸入流 32 Scanner scanner = new Scanner(reader); 33 // 將掃描到的字符流顯示在指定的JLabel上 34 while (scanner.hasNextLine()) { 35 try { 36 String line = scanner.nextLine(); 37 System.out.println("*****************************"+line); 38 //睡眠 39 Thread.sleep(100); 40 } catch (Exception ex) { 41 System.out.println("Exception from LogAppender:"+ex.getMessage()); 42 } 43 } 44 } 45 }
對比上面的日誌的配置,13行的構造函數就能夠傳入hermes來獲取hermes這個logger。
參考了下面的文檔:
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/NDC.html
https://logging.apache.org/log4j/2.x/manual/thread-context.html
http://yshjava.iteye.com/blog/1325036
https://stackoverflow.com/questions/2763740/log4j-log-output-of-a-specific-class-to-a-specific-appender
https://stackoverflow.com/questions/5549838/get-live-log4j-messages
https://sysgears.com/articles/how-to-redirect-stdout-and-stderr-writing-to-a-log4j-appender/