`
seandeng888
  • 浏览: 158146 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

源码赏析之深入了解log4j的LogManager类

阅读更多

    在上一篇博文中对log4j的结构有了一定的了解,这一篇就深入地了解一下其中的LogManager类。首先,先了解一下与LogManager相关的类结构;然后,再仔细地说明每个类的作用及其之间的关系。

1       类图



 

       图中的方框代表类,圆圈代表接口,实线箭头代表关联关系(箭头的一方为非箭头一方的属性),虚线箭头代表依赖关系(箭头的一方为非箭头一方的方法参数或局部变量),实线三角箭头表示继承关系。

从图中可知,LogManager类有RepositorySelector属性和LoggerRepository属性,并且依赖于Configurator接口。LoggerRepository类有LoggerFactory属性。DefaultLoggerFactory类有Logger属性。接下来简要介绍一下这些类和接口的作用。

LogManager类将ConfiguratorLoggerRepository整合在一起,它在初始化的时候(static)找到Log4J配置文件,并且将其解析到LoggerRepository中。

Configurator用来处理相关的配置文件。Log4J支持两种配置文件:properties文件和xml文件。

RepositorySelector负责缓存和管理LoggerRepository对象。

LoggerRepository是一个Logger的容器,它会调用LoggerFactory创建Logger并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。调用Configurator对配置文件进行解析,并将解析后的信息添加到LoggerRepository中。同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由LoggerName来决定,其中以”.”分隔。

HierarchyLog4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现。

2       RootLogger

       Log4J中,所有Logger都组装成以RootLogger为根的一棵树。

public final class RootLogger extends Logger { 

  /**

   * 使用日志级别来构造一个日志记录器,记录器名称默认为root

   */

  public RootLogger(Level level) {

    super("root");

    setLevel(level);

  }

}

3       LoggerFactory

        这里使用了创建型设计模式的工厂(Factory)模式。所有需要创建新Logger的方法都可以调用该接口的makeNewLoggerInstance方法。

/**

 *实现该接口可以创建新的日志记录器实例。

 */

public interface LoggerFactory {

  public Logger makeNewLoggerInstance(String name);

}

4      LoggerFactory的实现类

       该实现类为DefaultLoggerFactory

public final class DefaultLoggerFactory implements LoggerFactory {

  public DefaultLoggerFactory() {}

  public Logger makeNewLoggerInstance(String name) {

    return new Logger(name);

  }

}

5       Configurator

        Configurator用来处理相关的配置文件。Log4J支持两种配置文件:properties文件和xml文件。如下是配置器接口:

public interface Configurator {

}

6       Configurator的抽象实现类

        该抽象实现类是ConfiguratorBase类。

/**

 * 该类包含了一个大部分配置类都需要的功能。

 */

abstract public class ConfiguratorBase implements Configurator {

}

7       ConfiguratorBaseproperty实现类

       该实现类是PropertyConfigurator类,负责对property属性文件进行解析。

public class PropertyConfigurator extends ConfiguratorBase

    implements ConfiguratorEx {

}

8       ConfiguratorBaseXML实现类

    该实现类是JoranConfigurator类。负责对XML配置文件进行解析。

public class JoranConfigurator extends ConfiguratorBase

    implements ConfiguratorEx {

}

9       LoggerRepository接口

        LoggerRepository它是一个Logger的容器,它会调用LoggerFactory创建Logger并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。调用Configurator对配置文件进行解析,并将解析后的信息添加到LoggerRepository中。

        同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由LoggerName来决定,其中以”.”分隔。

        除此之外,它还有一个Threshold属性,用于过滤所有在Threshold级别以下的日志。以及其他和Logger操作相关的方法和属性。

public interface LoggerRepository {

  // 添加一个事件到repository.

  public void addHierarchyEventListener(HierarchyEventListener listener);

  public void setThreshold(Level level);

  public void setThreshold(String val);

  public Level getThreshold();

  public Logger getLogger(String name);

  public Logger getLogger(String name, LoggerFactory factory);

  public Logger getRootLogger();

  public abstract Logger exists(String name);

  public abstract void shutdown();

  public Enumeration getCurrentLoggers();

  public abstract void fireAddAppenderEvent(Category logger, Appender appender);

  public abstract void resetConfiguration();

}

10             LoggerRepository的子接口

public interface LoggerRepositoryEx extends LoggerRepository {

  void addLoggerRepositoryEventListener(LoggerRepositoryEventListener listener);

  void removeLoggerRepositoryEventListener(LoggerRepositoryEventListener listener);

  void addLoggerEventListener(LoggerEventListener listener);

  void removeLoggerEventListener(LoggerEventListener listener);

  String getName();

  void setName(String repoName);

  void fireRemoveAppenderEvent(Category logger, Appender appender);

  void fireLevelChangedEvent(Logger logger);

  void fireConfigurationChangedEvent();

  PluginRegistry getPluginRegistry();

  Map getProperties();

  String getProperty(String key);

  void setProperty(String key, String value);

  void setLoggerFactory(LoggerFactory loggerFactory);

  LoggerFactory getLoggerFactory();

}

11            LoggerRepositoryEx的实现类

        该实现类是Hierarchy类。

        HierarchyLog4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现。

Hierarchy中用一个Hashtable来存储所有Logger实例,它以CategoryKey作为keyLogger作为value,其中CategoryKey是对LoggerName字符串的封装,之所以要引入这个类是出于性能考虑,因为它会缓存Name字符串的hash code,这样在查找过程中计算hash code时就可以直接取得而不用每次都计算。

class CategoryKey {

  String name;

  int hashCache;

  CategoryKey(String name) {

    this.name = name;

    hashCache = name.hashCode();

  }

}

        getLogger()方法首先根据传入name创建CategoryKey实例,而后从缓存ht字段中查找:

1.       如果找到对应的Logger实例,则直接返回该实例。

2.       如果没有找到任何实例,则使用LoggerFactory创建新的Logger实例,并将该实例缓存到ht集合中,同时更新新创建Logger实例的parent属性。更新parent属性最简单的做法是从后往前以’.’为分隔符截取字符串,使用截取后的字符串从ht集合中查找是否存在Logger实例,如果存在,则新创建的Logger实例的parent即为找到的实例,若在整个遍历过程中都没有找到相应的parent实例,则其parent实例为root。然而如果一个“x.y.z.wLogger起初的parent设置为root,而后出现“x.y.zLogger实例,那么就需要更新“x.y.z.wLoggerparent为“x.y.zLogger实例,此时就会遇到一个如何找到在集合中已经存在的“x.y.zLogger实例子节点的问题。当然一种简单的做法是遍历ht集合中所有实例,判断那个实例是不是“x.y.zLogger实例的子节点,是则更新其parent节点。由于每次的遍历会引起一些性能问题,因而Log4J使用ProvisionNode事先将所有的可能相关的子节点保存起来,并将ProvisionNode实例添加到ht集合中,这样只要找到对应的ProvisionNode实例,就可以找到所有相关的子节点了。比如对“x.y.z.wLogger实例,它会产生三个ProvisionNode实例(当然如果相应的实例已经存在,则直接添加而无需创建,另外,如果相应节点已经是Logger实例,那么将“x.y.z.wLogger实例的parent直接指向它即可):ProvisionNode(x), ProvisionNode(x.y), ProvisionNode(x.y.z),他们都存储了“x.y.z.wLogger实例作为其子节点。

public class Hierarchy implements LoggerRepositoryEx, RendererSupport {

  private LoggerFactory loggerFactory;

  private final ArrayList repositoryEventListeners;

  private final ArrayList loggerEventListeners;

  String name;

  Hashtable ht;  // Loggers所在的Hashtable.

  Logger root;  // Root logger.

  /**

   * 构造一个logger hierarchy.

   */

  public Hierarchy(final Logger rootLogger) {

    ht = new Hashtable();

    repositoryEventListeners = new ArrayList(1);

    loggerEventListeners = new ArrayList(1);

    this.root = rootLogger;

    this.objectMap = new HashMap();

    // Enable all level levels by default.

    setThreshold(Level.ALL);

    this.root.setHierarchy(this);

    rendererMap = new RendererMap();

    rendererMap.setLoggerRepository(this);

    properties = new Hashtable();

    loggerFactory = new DefaultLoggerFactory();

  }

  public Logger getLogger(final String loggerName) {

    return getLogger(loggerName, loggerFactory);

  }

  public Logger getLogger(final String loggerName, final LoggerFactory factory) {

    CategoryKey key = new CategoryKey(loggerName);

    Logger logger;

    synchronized (ht) {

      Object o = ht.get(key);

      if (o == null) {

        LogLog.debug("Creating new logger [" + loggerName + "] in repository [" + getName() + "].");

        logger = factory.makeNewLoggerInstance(loggerName);

        logger.setHierarchy(this);

        ht.put(key, logger);

        updateParents(logger);

        return logger;

      } else if (o instanceof Logger) {

          LogLog.debug("Returning existing logger [" + loggerName + "] in repository [" + getName() + "].");

          return (Logger) o;

      } else if (o instanceof ProvisionNode) {

        logger = factory.makeNewLoggerInstance(loggerName);

        logger.setHierarchy(this);

        ht.put(key, logger);

        updateChildren((ProvisionNode) o, logger);

        updateParents(logger);

        return logger;

      } else {

        // 代码运行时不会到达这里。

        return null; // 但是这样可以避免编译器告警。

      }

    }

  }

}

12             RepositorySelector接口

    RepositorySelector负责缓存和管理LoggerRepository对象。

public interface RepositorySelector {

  public LoggerRepository getLoggerRepository();

}

13             RepositorySelector的子接口

        该子接口为RepositorySelectorEx 接口。

public interface RepositorySelectorEx extends RepositorySelector {

  LoggerRepository detachRepository(String name);

}

14             RepositorySelectorEx的实现类

        该实现类为DefaultRepositorySelector类。

public class DefaultRepositorySelector implements RepositorySelectorEx {

  private LoggerRepository defaultRepository;

  public DefaultRepositorySelector(final LoggerRepository repository) {

    if (repository == null)

      throw new NullPointerException();

    this.defaultRepository = repository;

  }

  public LoggerRepository getLoggerRepository() {

    return defaultRepository;

  }

}

15             LogManager

        LogManagerConfiguratorLoggerRepository整合在一起,它在初始化的时候(static)找到Log4J配置文件,并且将其解析到LoggerRepository中。

public class LogManager {

    private static RepositorySelector repositorySelector;

    private static boolean debug = false;

    /**

     * 默认的LoggerRepository实例。

     */

public static final LoggerRepository defaultLoggerRepository;

// 初始化部分。

    static {

       String debugProp = System.getProperty("log4j.debug");

       if(Boolean.valueOf(debugProp).booleanValue()) {

          debug = true;

       }

       if(debug) {

          System.out.println("**Start of LogManager static initializer");

       }

       Hierarchy hierarchy =  new Hierarchy(new RootLogger(Level.DEBUG));

       defaultLoggerRepository = hierarchy;

       hierarchy.setName(Constants.DEFAULT_REPOSITORY_NAME);

       repositorySelector = new DefaultRepositorySelector (defaultLoggerRepository);

       String configuratorClassName = OptionConverter.getSystemProperty(Constants.CONFIGURATOR_CLASS_KEY, null);

       String configurationOptionStr = OptionConverter.getSystemProperty(Constants.DEFAULT_CONFIGURATION_KEY, null);

       if (configurationOptionStr == null) {

          if (Loader.getResource(Constants.DEFAULT_XML_CONFIGURATION_FILE) != null) {

               configurationOptionStr = Constants.DEFAULT_XML_CONFIGURATION_FILE;

            } else if (Loader.getResource(Constants.DEFAULT_CONFIGURATION_FILE) != null) {

               configurationOptionStr = Constants.DEFAULT_CONFIGURATION_FILE;

                }

            }

        if(debug) {

            System.out.println("*** configurationOptionStr=" + configurationOptionStr);

        }

        // 整合ConfiguratorLoggerRepository部分。

        IntializationUtil.initialConfiguration(defaultLoggerRepository, configurationOptionStr, configuratorClassName);

        String repositorySelectorStr = OptionConverter.getSystemProperty("log4j.repositorySelector", null);

        if (repositorySelectorStr == null) {

            // 不做任何事情, 默认的repository已经在前面配置好了。

        } else if (repositorySelectorStr.equalsIgnoreCase("JNDI")) {

            if(debug) {

                System.out.println("*** Will use ContextJNDISelector **");

            }

            repositorySelector = new ContextJNDISelector();

            guard = new Object();

        } else {

            Object r = OptionConverter.instantiateByClassName(repositorySelectorStr, RepositorySelector.class, null);

            if (r instanceof RepositorySelector) {

                if(debug) {System.out.println("*** Using [" + repositorySelectorStr+ "] instance as repository selector.");

                }

                repositorySelector = (RepositorySelector) r;

                guard = new Object();

            } else {

                if(debug) {System.out.println("*** Could not insantiate [" + repositorySelectorStr+ "] as repository selector.");

                    System.out.println("*** Using default repository selector");

                }

                repositorySelector = new DefaultRepositorySelector(defaultLoggerRepository);

            }

        }

        if(debug) {

            System.out.println("** End of LogManager static initializer");

        }

    }

public static Logger getLogger(String name) {

    return repositorySelector.getLoggerRepository().getLogger(name);

    }

 

}

  • 大小: 29.3 KB
1
1
分享到:
评论

相关推荐

    老生常谈Log4j和Log4j2的区别(推荐)

    然后使用Logger.getLogger()方法获取日志记录器,而Log4j2需要import org.apache.logging.log4j.Level、org.apache.logging.log4j.LogManager和org.apache.logging.log4j.Logger,使用LogManager.getLogger()方法...

    log4j-1.2.13-src

    Apache Log4j是Java平台上广泛使用的日志记录框架,其1.2.13版本的源码为我们提供了深入了解这个强大工具的机会。本文将围绕log4j的核心概念、设计模式、主要组件以及其实现细节进行详细的探讨。 一、Log4j概述 ...

    org.apache.log4j

    Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式...

    log4j需要jar

    这个错误意味着系统在运行时找不到指定的类,即LogManager类,这是Log4j的核心管理类,负责初始化日志系统和管理日志配置。 **Log4j的基本结构** Log4j主要包括三个主要组件:Logger(日志器)、Appender(输出端...

    apache-log4j-2.9.1jar包及源码

    通过阅读源码,可以深入了解Log4j的工作原理,如何配置和定制日志系统,以及如何解决特定问题。源码中还包含了单元测试,这些测试可以作为使用Log4j功能的实例,帮助理解其内部机制。 使用Log4j的主要优点包括: 1...

    log4j-api-2.17.1.jar和log4j-core-2.17.1.jar

    《深入理解log4j-api-2.17.1.jar与log4j-core-2.17.1.jar》 在Java开发中,日志管理是不可或缺的一部分,它帮助我们跟踪程序运行状态、定位错误和调试问题。Log4j作为一款广泛使用的日志框架,历经多次迭代,现在...

    log4j记录Debug日志

    在使用Log4j记录Debug日志时,确保项目中引入了相应的Log4j库,并正确配置了`log4j.properties`或`log4j.xml`文件,设置DEBUG级别,然后在代码中使用Logger对象进行日志记录。例如: ```java import org.apache.log4...

    Log4j2异步写日志源码

    本文将深入探讨如何在Log4j2中实现异步写日志,并通过提供的文件`TestController.java`、`log4j2.xml`和`pom.xml`来解析具体实现。 首先,`pom.xml`是Maven项目的配置文件,它定义了项目依赖。为了使用Log4j2,我们...

    apache-log4j-2.17.0-bin.tar.gz

    通常,这会涉及到创建一个配置文件(如log4j2.xml或log4j2.json),并设置环境变量或类路径来指向这个配置文件。在Java代码中,你可以通过调用`org.apache.logging.log4j.LogManager.getLogger()`获取一个Logger实例...

    log4j 介绍

    Log4j的启动过程通常会在程序中自动进行,如果没有显式初始化Log4j配置文件,LogManager类中的静态块会在classpath目录下查找log4j.xml文件或log4j.properties文件。如果需要自定义配置文件的路径,可以使用...

    示范如何在android工程中使用log4j记录日志

    3. **初始化Log4j**:在Application类的onCreate方法中初始化Log4j,确保在使用日志之前完成配置加载: ```java @Override public void onCreate() { super.onCreate(); Log4jConfig.init(this); } ``` 4. *...

    log4j2 -2.11.1.zip

    在本篇中,我们将深入探讨Log4j2的核心特性、其API与Core组件的功能,以及如何在实际项目中应用这些文件。 Log4j2是一个继Log4j之后的日志系统,旨在提供更高效的性能、丰富的日志配置选项和动态日志管理。2.11.1...

    Apache Log4j_1.2.17 完整依赖包

    Apache Log4j_1.2.17 完整依赖包,在jdk1.8.201中测试通过。使用教程https://www.tutorialspoint.com/springmvc/springmvc_log4j.htm

    log4j相关jar包

    本文将深入探讨“log4j相关jar包”中的关键组件,包括log4j、SLF4J(Simple Logging Facade for Java)以及log4j2,以及它们在实现日志输出功能中的作用。 **log4j** Log4j是Apache软件基金会的一个开源项目,它是...

    log4j2.xml记录日志到到数据库

    import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Main { private static final Logger logger = LogManager.getLogger(Main.class); public static void ...

    Log4j api 下载

    **Log4j API 下载** Log4j 是一个广泛使用的Java日志框架,它为应用程序提供了灵活的日志记录功能。这个框架允许开发者控制日志信息的输出格式、输出位置以及日志级别,使得调试和故障排查更为高效。本文将详细介绍...

    spring log4j 实例

    你可以根据这些文件进一步了解如何将Spring和Log4j整合在一起,以及如何在实际项目中应用这个实例。记得检查代码中的注释,它们通常会提供关于如何运行和测试实例的重要信息。 通过上述步骤,你就可以在Spring应用...

    log4j与spring的详细配置。

    <property name="staticMethod" value="org.apache.log4j.LogManager.getLoggerRepository"/> <value>org.apache.commons.logging.LogFactory ``` 2. **自动加载配置**:可以通过`...

    log4j从入门到详解

    Log4j正是为此而设计的强大工具之一。 #### 2. 下载与使用 ##### 2.1 下载log4j的jar文件 首先需要从官方网站下载log4j的核心库。目前,Log4j有两个主要版本:Log4j 1.x 和 Log4j 2.x。对于新手来说,建议从Log4j...

    Log4j2 web demo (log4j2 version 2.1)

    在Log4j2版本2.1中,引入了更多性能优化和新特性,使其成为日志管理的首选工具之一。本Demo旨在展示如何在Web项目中集成和使用Log4j2。 **1. 安装与配置** 首先,你需要在你的Maven或Gradle项目中添加Log4j2的依赖...

Global site tag (gtag.js) - Google Analytics