Log4J日志分级应用

本文主要讲述一个日志分级应用的模型和如何利以及扩展Log4J来达到目的,关于Log4J的配置说明和基础应用就不再累述,详情可参见官方文档http://logging.apache.org/log4j/1.2/manual.html

 

另外3篇比较详细的博文:

首先我们来看一个模型:


 

KernelApplications都运行于同一个JVM中,且Applications运行于Kernel之上。JVM启动时依次会创建Bootstrap ClassLoader, Extension ClassLoaderAppClassLoader,分别会去加载JRE/lib,JRE/lib/ext下的核心包和kernel以及相关依赖的classes,jars等,之后由kernel创建并加载各个application,整个过程和一般的java web服务器启动比较类似(tomcat)

 

自然而然就有了这样的日志分级需求:

 

  • Kernel的日志记录到kernel.log文件中
  • 各个Application的日志分别记录到各自的文件中,如BBS的日志记录到bbs.logMails的日志记录到mails.log文件中
  • 其它日志(所有非kernelapplication中所关心的日志)记录到root.log中,以备不时之需

 

那么接下我们就要考虑log4j的配置问题了?——在这里我使用的配置文件是log4j.properties

 

  1. log4j.properties文件的位置?
  2. log4j.properties文件的内容?

对于问题1,从2个方面考虑:

 

  1. kernel中有使用log4j,并且可以kernel独立,所以log4j.properties需要在kernel中存在
  2. kernel并不知将来有多少applications,所以关于application日志的配置信息可以:
    •  采用默认机制创建,即在kernel加载application时创建
    • application中也加入log4j的配置信息,加载时读取

此外我们已经分析了整个模型的类加载机制,很显然log4j.jar也只会被kernel加载一次。看过log4j1.2.16)源代码的同学都知道log4j的初始化是在静态块中进行的,也就是说log4j.properties只会被加载一次,那么对于application中的配置文件就需要我们自己用log4j提供的APIkernel加载application时进行初始化了。

 

 

有了问题1的分析,我们可以把模型稍微简化成下图后再来分析问题2


 

依然是要求app1包下的日志写到app1.log文件中,app2包下的日志写到app2.log文件中,kernel包下的日志写到kernel.log文件中,其余的日志则写到root.log文件中。

 

log4.properties则如下所示


 

运行得到了以下日志输出:

 

不同package下的日志输出到了不同的日志文件中,看起来目的达到了,不过还是存在问题——即便rootlog级别设置为了error,但kernelapplication中的info信息还是会被输出到root.log中。大家可以想一下这是为什么?

 

 

如何避免写到kernelapplication中的日志重复被写到root中呢?——关键是org.apache.log4j.Category中的方法callAppenders

 /**
     Call the appenders in the hierrachy starting at
     <code>this</code>.  If no appenders could be found, emit a
     warning.

     <p>This method calls all the appenders inherited from the
     hierarchy circumventing any evaluation of whether to log or not
     to log the particular log request.

     @param event the event to log.  */
  public
  void callAppenders(LoggingEvent event) {
    int writes = 0;

    for(Category c = this; c != null; c=c.parent) {
      // Protected against simultaneous call to addAppender, removeAppender,...
      synchronized(c) {
	if(c.aai != null) {
	  writes += c.aai.appendLoopOnAppenders(event);
	}
	if(!c.additive) {
	  break;
	}
      }
    }

    if(writes == 0) {
      repository.emitNoAppenderWarning(this);
    }
  }
 

我们最终的目的就是阻止其输出日志后还继续上溯输出,因此需做如下修改:

if(c.aai != null) {
	  writes += c.aai.appendLoopOnAppenders(event);
          break;
	}
 

 

除了修改log4j其原生代码外,我们还可以通过继承和扩展的手段实现,下面便是所有实现代码。

  • 扩展org.apache.log4j.Logger和DefaultLoggerFactory如下
package com.xxx.cases
public class DefaultLoggerFactory implements org.apache.log4j.spi.LoggerFactory {

  public DefaultLoggerFactory() {
  }

  public Logger makeNewLoggerInstance(String name) {
    return new Logger(name);
  }
}

 

 

package com.xxx.cases;

import org.apache.log4j.Appender;
import org.apache.log4j.helpers.AppenderAttachableImpl;
import org.apache.log4j.spi.LoggingEvent;

public class Logger extends org.apache.log4j.Logger{
    private static final DefaultLoggerFactory defaultFactory = new DefaultLoggerFactory();

    private AppenderAttachableImpl aai;

    protected Logger(String name) {
        super(name);
    }

    @Override
    public void callAppenders(LoggingEvent event) {
        if (this.aai == null && !(this.getParent() instanceof Logger)) {
            super.callAppenders(event);
        } else {
            int writes = 0;

            for (Logger c = this; c != null; c = (Logger) c.getParent()) {
                // Protected against simultaneous call to addAppender, removeAppender,...
                synchronized (c) {
                    if (c.aai != null) {
                        writes += c.aai.appendLoopOnAppenders(event);
                        break;
                    }
                    if (!c.getAdditivity()) {
                        break;
                    }
                }
            }

            if (writes == 0) {
                repository.emitNoAppenderWarning(this);
            }
        }
    }

    public synchronized void addAppender(Appender newAppender) {
        if (aai == null) {
            aai = new AppenderAttachableImpl();
        }
        aai.addAppender(newAppender);
        repository.fireAddAppenderEvent(this, newAppender);
    }

    public static Logger getLogger(Class clazz){
        return (Logger)org.apache.log4j.Logger.getLogger(clazz.getName(), defaultFactory);
    }
}
  • 修改log4j.properties配置文件,增加loggerFactory配置项

 

  • 新Logger的应用(如在kernel中)

package com.xxx.cases.kernel;

import com.xxx.cases.Logger;

public class KernelBean {
    private static Logger logger = Logger.getLogger(KernelBean.class);

    public void testMethod(){
        logger.info("in kernel bean test method");
    }
}

 

后记:经朋友提醒,还有一种更加简便的方式,只需要修改log4.properties配置信息——其目的和原理是一样的。



 注意黄色高亮的部分。

 

 

——结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值