拓展Log4j基本尝试.md

来源:互联网 发布:mac下安装启动盘 编辑:程序博客网 时间:2024/06/02 02:01

上周老大让我拓展下apache的log4j,让其可以把日志内容当做事件,发送到消息队列当中,然后有专门的事件处理来订阅消息,同时事件处理程序和socket关联在一块。客户端通过socket请求日志信息,请求参数构成规则,来决定是否通过socket连接发送日志到客户端。

现在我们把焦点放在“如何在打印日志的时候发消息”,至于事件处理程序暂且不表。
这是一条基本日志信息:
2017-09-05 22:59:00 DEBUG [org.apache.activemq.util.ThreadPoolUtils] Forcing shutdown of ExecutorService: java.util.concurr
我最开始的想法是,将日志内容分为分为4部分:日期,级别,所属类,日志内容。用这4个内容作为规则判断。创建一个Log类:

public class Log implements  Serializable{    protected Date date;//log日期    protected Level level;//log的级别    protected String clazz;//log所属的类全称    protected Object message;//log的内容

那么如何在打印日志的时候,获取到日志的这4个部分呢?我说一下我的思考过程。最开始打算包装Logger,在日志打印的最外层发送log事件;后来又尝试从打印的日志结果截取log对象发送;这些想法都比较蠢,没有从根本解决问题。昨天了解了log4j的执行流程,还有配置文件的规则和作用,找到了一种非常简单有效的实现方式,直达问题的本质。

一.简单的包装Logger类,在最外层发送log事件

public class NewLogger extends Logger {    private Logger logger;    public NewLogger(Logger logger){        super(logger.getName());        this.logger = logger;    }    public static NewLogger getNewLogger(Class clazz){        Logger log = getLogger(clazz);        //TODO 可能还要检查NewLogger对象是否存在,存在就获取        return new NewLogger(log);    }    @Override    public void trace(Object message) {        logger.trace(message);        sendLogEvent("TRACE", message);    }    @Override    public void debug(Object message){        logger.debug(message);        sendLogEvent("DEBUG", message);    }    @Override    public void info(Object message) {        logger.info(message);        sendLogEvent("INFO", message);    }    @Override    public void warn(Object message) {        logger.warn(message);        sendLogEvent("WARN", message);    }    @Override    public void error(Object message) {        logger.error(message);        sendLogEvent("ERROR", message);    }    @Override    public void fatal(Object message) {        logger.fatal(message);        sendLogEvent("FATAL", message);    }    /**     * 载入发事件的动作     *     */    protected void sendLogEvent(String level, Object message) {        String className = logger.getName();        Level level_ = Level.toLevel(level);        Log log = new Log(new Date(),level_,className,message);        //发送事件     //sendEvent(log);    }

这是最基本的思路,但是有明显的缺陷。第一,用NewLog构建的对象才能发消息,其他如框架,组件,库等使用Logger,或LoggerFactory的地方任然无法发送事件;第二NewLog是使用Logger对象完成日志的处理,Logger对象可以保证唯一性,同一个class多次构建Logger对象(Logger.getLogger(this.getClass());),也只会生成一个Logger对象,但是NewLogger还需要自己做校验,比较麻烦;第三,时间不准,上面的处理中是打印log之后,new了一个date对象作为时间,虽然有毫秒级的误差,但是终究不是正确的。

二、监听控制台Appender,得到一行行日志信息,将其格式化

看一下log4j.properties配置

log4j.rootLogger=debug, logFile, stdoutlog4j.appender.logFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.logFile.DatePattern='.'yyyy-MM-dd log4j.appender.logFile.File=C://log/spring.log log4j.appender.logFile.layout=org.apache.log4j.PatternLayout log4j.appender.logFile.layout.ConversionPattern=%d - %m%n log4j.appender.logFile.Append=truelog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%c] %m%n

我们获取stdout的Appender,监听它的输出,从管道中读出来

public void getLogInfo(){    Logger root = LogManager.getRootLogger();    if(root==null)    return ;    // 获取子记录器的输出源    Appender appender = root.getAppender("stdout");    // 定义一个未连接的输入流管道    PipedReader reader = new PipedReader();    // 定义一个已连接的输出流管理,并连接到reader    Writer writer = null;    try {        writer = new PipedWriter(reader);    } catch (IOException e) {        e.printStackTrace();    }    // 设置 appender 输出流    ((WriterAppender) appender).setWriter(writer);    Scanner scanner = new Scanner(reader);    while ( scanner.hasNextLine()) {        try {            //睡眠       Thread.sleep(100);            String line = scanner.nextLine();            System.out.println("[+]================"+line);            //TODO 格式化line得到Log对象         ;        } catch (Exception ex) {            //异常信息不作处理        }    }}

这是一种本末倒置的做法,强烈不推荐。

这篇文章就到这里,下篇我会将一种聪明的做法。

原创粉丝点击