基于log4j的消息流的实现之一消息获取

Posted 029zz010buct

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于log4j的消息流的实现之一消息获取相关的知识,希望对你有一定的参考价值。

需求:

目前的程序中都是基于log4j来实现日志的管理,想要获取日志中的一部分消息,展示给用户。

约束:

由于程序中除了自己开发的代码,还会有层层依赖的第三方jar中的日志输出。需要展示给用户的消息,也有部分包括在第三方的包中。不可能去修改第三方jar来获得消息,所以只能从Log4j本身的消息入手,获取log4j的消息来进行处理。

方案:

第一步,增加新的logger

 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。

关键是第7行,又定义了一个叫hermes的logger,它有一个同名的appender叫hermes,简化了他的layout,只是拿出必须的输出日志即可。注意这里有一个%X{key}的配置项,这里是为了拿到MDC的值,好区分这条消息是哪个session打印出来的。

第12行也很关键,将包org.apache.flink.yarn的日志都按照hermes是标准输出,这里是包名,当然也可以指定到具体的类名。

如此这样,对于包org.apache.flink.yarn中的日志,会打印两份,一份是以console的形式输出,一份是以hermes的形式输出。

当然,我们不希望在日志中见到两行实质上一样的内容,这就是第二步要做到事情。

第二步:截取消息

这里有两个方案:一个方案是自定义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     protected 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                 //睡眠
37                 Thread.sleep(100);
38                 String line = scanner.nextLine();
39                 System.out.println("*****************************"+line);
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/

以上是关于基于log4j的消息流的实现之一消息获取的主要内容,如果未能解决你的问题,请参考以下文章

Redis Stream 流的深度解析与实现高级消息队列一万字

Redis Stream 流的深度解析与实现高级消息队列一万字

Redis Stream 流的深度解析与实现高级消息队列一万字

用于双向消息流的 HTTP/2 与 web-sockets

面向消息的持久通信与面向流的通信简要总结

设备未收到 GCM 消息