Log4j源码阅读之四—doConfigure方法

本文详细解读Log4j的doConfigure方法,包括attachListAppender、configureRootCategory、configureLoggerFactory、parseCatsAndRenderers、clearRegistry和detachListAppender等步骤。讲解了如何配置根日志器、解析类别和渲染器,以及在Log4j配置文件中的关键操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在阅读  public void doConfigure(Properties properties, LoggerRepository repository) 这个方法的源码的时候,我这里就不讲源码再粘贴出来了,我们

大概了解这个方法分别调用如下几个主要的方法,其他都是一些判断

1,attachListAppender(repository);

2,configureRootCategory(properties, repository);

3,configureLoggerFactory(properties, repository);

4, parseCatsAndRenderers(properties, repository);

5,clearRegistry();

6,detachListAppender(repository);

我们分别到这几个方法的源码中去了解下,他们究竟做了什么:

一,attachListAppender(LoggerRepository repository)

     //我们根据之前的章节应该知道,传入的LoggerRepository接口实现类Hierarchy在实现getLogger的方法的时候,判断ht集合中是否存在相同的实例,如果不存在

    //会创建以参数为名称的Logger实例,并将这些实例放到ht集合中。所以我们可以知道,这里就是往ht集合里添加了对应的logger对象

     Logger ll = repository.getLogger(Constants.LOG4J_PACKAGE_NAME);

    //创建一个ListAppender实例,并为他起了个Constants.TEMP_LIST_APPENDER_NAME名字

    Appender appender = new ListAppender();
    appender.setName(Constants.TEMP_LIST_APPENDER_NAME);

   //如何将这个appender添加到logger实例中
    ll.addAppender(appender);
    ll.setAdditivity(false);

总上述,我们很快就知道,这里就是往ht集合里边添加了含有append实例的logger对象。

二,configureRootCategory(Properties props, LoggerRepository repository)

    String effectiveFrefix = ROOT_LOGGER_PREFIX;

    //在props对象中获取log4j.rootLogger配置的值
    String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
   
    if (value == null) {
      value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
      effectiveFrefix = ROOT_CATEGORY_PREFIX;
    }


    if (value == null) {
      getLogger(repository).debug(
        "Could not find root logger information. Is this OK?");
    } else {

      //如果存在,那么我们从Hierachy实例里边获取Logger的实例,如果我们还有记得,这个时候,获取就是我们

    //创建Hierachy实例时创建的RootLogger实例
      Logger root = repository.getRootLogger();
  
     //对root实行同步锁,然后调用parseCategory方法
      synchronized (root) {
        parseCategory(
          repository, props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
      }
    }

这个方法里边做的事就比较复杂了:

1,根据传递进来的log4j.rootLogger配置的值,通常这个值用逗号“,”分割开。分割开的第一个参数就是调试的等级debug等值,具体有哪些值可以设置,我们可以查看

     Level类的常量值

2,然后将这个等级赋值给当前logger对象

3,然后logger对象,将其属性中的AppenderAttachableImpl实例中的Appender进行清除。其实AppenderAttachableImpl实例我们可以看做是封装了Vector的集合

4,然后我们继续迭代log4j.rootLogger配置的值,每迭代一次就获取配置项对应的一个appender实例和Layout实例配置,并将Layout实例放入appender实例中,然后

      压入当前Logger对象的AppenderAttachableImpl实例中Vector中。我们举个例子来解析下段代码:

      假如我们的配置项如下配置:

      log4j.rootLogger=INFO, Console, D, E

     那么我们在4进行迭代的时候,

   第一次循环

    a, 获取appenderName=Console

    b,查询配置项里边是否存在log4j.appender.Console配置的值,如果有那么就加载并实例化一个实例,如果没有就实例化一个Append的实例

    c,同时将这个实例化的AppenderName设置为Console

    d,查询配置项里面是否存在log4j.appender.Console.layout的配置值,如果有那么就进行实例化一个实例,没有就实例化一个Layout的实例

    e,将d实例化的layout赋值给b实例化的append属性中

    f, 将实例化的append放入到Hashtable实例registry中

    g,将封装好的append压入当前Logger对象的AppenderAttachableImpl实例中Vector中

  第二次循环

    a, 获取appenderName=D

    b,查询配置项里边是否存在log4j.appender.D配置的值,如果有那么就加载并实例化一个实例,如果没有就实例化一个Append的实例

    c,同时将这个实例化的AppenderName设置为D

    d,查询配置项里面是否存在log4j.appender.D.layout的配置值,如果有那么就进行实例化一个实例,没有就实例化一个Layout的实例

    e,将d实例化的layout赋值给b实例化的append属性中

    f, 将封装好的append压入当前Logger对象的AppenderAttachableImpl实例中Vector中

以此类推。

我们查看下append接口的继承关系:


我们再查看下Layout这个抽象类的继承关系:


从这2者的继承可以知道,我们在配置的时候,对

log4j.appender

log4j.appender.xxx.layout的配置值是蛮多的,究竟他们的功能如何,这里就不深入去了解先了。

三,configureLoggerFactory(Properties props, LoggerRepository repository)

     //获取log4j.loggerFactory配置项的值

    String factoryClassName =
      OptionConverter.findAndSubst(LOGGER_FACTORY_KEY, props);

  //如果不为空,那么就实例化
    if (factoryClassName != null) {
      loggerFactory =
        (LoggerFactory) OptionConverter.instantiateByClassName(
          factoryClassName, LoggerFactory.class, loggerFactory);
      PropertySetter setter = new PropertySetter(loggerFactory);
      setter.setLoggerRepository(repository);
      setter.setProperties(props, FACTORY_PREFIX + ".");
    }

四, parseCatsAndRenderers(Properties props, LoggerRepository repository)

    Enumeration enumeration = props.propertyNames();
   //1,迭代所有配置项
    while (enumeration.hasMoreElements()) {
      String key = (String) enumeration.nextElement();

     //2,判断是否含有log4j.category.和log4j.logger.配置项开头的
      if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
        String loggerName = null;


        if (key.startsWith(CATEGORY_PREFIX)) {
          loggerName = key.substring(CATEGORY_PREFIX.length());
        } else if (key.startsWith(LOGGER_PREFIX)) {
          loggerName = key.substring(LOGGER_PREFIX.length());
        }

       //3,根据loggerName产生Loger对象
        String value = OptionConverter.findAndSubst(key, props);
        Logger logger = repository.getLogger(loggerName, loggerFactory);

      //4,如何在调第二步里边的parseCategory逻辑
        synchronized (logger) {
          parseCategory(repository, props, logger, key, loggerName, value);
          parseAdditivityForLogger(repository, props, logger, loggerName);
        }
      } else if (key.startsWith(RENDERER_PREFIX)) {

       //5,如果含有log4j.renderer.前缀的配置项,那么就进行实例或获取,并放入到logRepository对象的属性中
        String renderedClass = key.substring(RENDERER_PREFIX.length());
        String renderingClass = OptionConverter.findAndSubst(key, props);


        if (repository instanceof RendererSupport) {
          RendererSupport rs = (RendererSupport) repository;
          RendererMap rm = rs.getRendererMap();
          rm.addRenderer(renderedClass, renderingClass);
        }
      }
    }

五,clearRegistry() 

  这里只将在第二步放入registry这个HashTable实例集合中的对象进行清空

六,detachListAppender(LoggerRepository repository)

  这里只是将第一步放入到Logger对象中的ListAppender对象进行清空

至此,LogManager静态代码块的代码基本阅读完毕,那么我们就来进行总结下:

1,在静态代码块做了首先是产生Logger,LogRepository和LogRepositorySelector对应的实体,并将这三个实体进行了结合即:

      RootLogger实体作为参数传递给Hierarchy的属性中,同时将Hierarchy实例作为DefaultRepositorySelector的属性

2,读取配置文件log4j.properties,并将其转为properties对象

3,读取配置项中的log4j.rootLogger配置值,如何循环产生layout对象赋值给循环产生的append实例属性中,最后将所有的append实例压入Logger对象的属性的Vetor集合中。

4,如何进行了一些清理收尾工作。

5,在这阅读过程中,还有些具体的配置为在进行细致的研读,但是我们可以从这里的层级关系可以知道,其他配置项也必将是按照这种层级关系往下放置的。

LogManager类的静态程序块就完成了,Log4j的加载过程也就完成了,

我们下一回就通过Logger对象的debug方法即logger.debug("Hello world.");来进行调试日志输出源码的也读。

Log4j源码阅读之五—logger.debug("Hello world.");


  
  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值