`
Fly_m
  • 浏览: 259694 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Log4j启动过程

    博客分类:
  • java
阅读更多

    用了好久的log4j,但还是不知道Log4j究竟是基于怎样的原理来进行工作,以及为何在项目中除了Log4j之外,还需要一个common-logging来协同进行日志记录。在网上看了下相应介绍,都说common-logging是一个日志的管理框架,具体的事情还是交由log4j来进行记录。决定从源码出发,看看Log4j如何加载配置文件,并进行日志记录。

    将Log4j从网上down下来,并建立工程,将源代码导进去。从Logger入手,一般来说,取得一个Log都是通过

Logger getLogger(Class clazz) {
    return LogManager.getLogger(clazz.getName());
 }

 

转入LogManager,首先应该注意的是这个类的static块, 这个块其实就是一个加载log4j配置文件的过程。即在程序启动之初,在JVM需要加载这个类时,这个初始化块会自动运行,并且加载整个配置,以完成log4j的启动。

Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
......
OptionConverter.selectAndConfigure(url, configuratorClassName,
					   LogManager.getLoggerRepository());

 

上面的所有代码均完成两件事,第一件事就是构造一个ROOT的Logger对象,此对象作为所有logger对象的最上层,其它logger的相应属性均从这个对象进行继承或改写,就好像java里的继承一样,这个类是内定的,不能由配置文件直接指定,且root都是在最顶层的,其他logger均在此下,这样形成一个完整的logger树。下层可以引用上层,上层管理下层。    第二件事则是去寻找配置文件的地址信息,通过各种方法都寻找log4j.xml或log4j.properties文件,然后对文件进行解析。(在此处,通过properties文件进行解析)

OptionConverter.void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy)

 这个方法会最终通过指定的文件解析类(此处是PropertyConfigurator)进行解析,转入

configurator.doConfigure(url, hierarchy)

 这个方法将,url转化成一个properties对象,进行解析。

doConfigure(props, hierarchy);

 进入这个方法

String value = properties.getProperty(LogLog.DEBUG_KEY);//即log4j.debug
    if(value == null) {
      value = properties.getProperty("log4j.configDebug");
      if(value != null) {
      LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
    }

  上面方法读取一个关于log4j自身的debug Level信息,主要用于log4j内部在解析时调用(因为log4j还不能使用logger对象进行写信息,它用到一个LogLog的类,来模拟logger记录,用于在自身解析的过程中输出一些信息),一般来说,这个都用不到。

    主要的信息集中在以下三句话:

configureRootCategory(properties, hierarchy);
configureLoggerFactory(properties);
parseCatsAndRenderers(properties, hierarchy);

 

第一句话用于解析root根对象上的相关配置。

第二句话用于解析loggerFactory(factory用于创建logger对象)

第三句话用于解析除rootLogger之外的其他logger以及render信息。

 

第一句:

  void configureRootCategory(Properties props, LoggerRepository hierarchy) {
    String effectiveFrefix = ROOT_LOGGER_PREFIX;
    String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
    if(value == null) {
      value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
      effectiveFrefix = ROOT_CATEGORY_PREFIX;
    }
......
      Logger root = hierarchy.getRootLogger();
      synchronized(root) {
	parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
      }
  }

 

上面语句用于从属性文件中寻找logger.rootLogger或logger.rootCategory属性的值然后,再进行配置rootLogger对象信息(如Level,appender等)

  void parseCategory(Properties props, Logger logger, String optionKey,
		     String loggerName, String value) {

  StringTokenizer st = new StringTokenizer(value, ",");
    if(!(value.startsWith(",") || value.equals(""))) {
      if(!st.hasMoreTokens())
	return;

      String levelStr = st.nextToken();
      if(INHERITED.equalsIgnoreCase(levelStr) || 
 	                                  NULL.equalsIgnoreCase(levelStr)) {
	if(loggerName.equals(INTERNAL_ROOT_NAME)) {
	  LogLog.warn("The root logger cannot be set to null.");
	} else {
	  logger.setLevel(null);
	}
      } else {
	logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG));
      }

    logger.removeAllAppenders();

    Appender appender;
    String appenderName;
    while(st.hasMoreTokens()) {
      appenderName = st.nextToken().trim();
......
      appender = parseAppender(props, appenderName);
      if(appender != null) {
	logger.addAppender(appender);
      }
    }
  }

 

上面这些内容,即是通过logger属性的值(形如debug,A,R)等,然后将值以,分隔进行解析,将第一个字符串定义为logger的Level信息,后面的值则定义为此logger的appender名称。

    当然上面这个方法,即不是尽为rootLogger服务,对于其他logger也调用这个方法,故上面在解析Level时,对root作了单独判断(因为rootLogger的Level不能为空,至少均需要一个值)。处理Level,即将level值简单设置在logger上即可以了。

   接下来即解析appender信息,通过紧接着level后面的字符串,按列表方式进行解析,然后将appender加入logger的appenderList(即要进行信息处理的监听器表)中。进入parseAddpender(解析appender)

    String prefix = APPENDER_PREFIX + appenderName;
    String layoutPrefix = prefix + ".layout";

    appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
					      org.apache.log4j.Appender.class,
					      null);
......
    appender.setName(appenderName);

    if(appender instanceof OptionHandler) {
      if(appender.requiresLayout()) {
	Layout layout = (Layout) OptionConverter.instantiateByKey(props,
								  layoutPrefix,
								  Layout.class,
								  null);
......
	  appender.setLayout(layout);
          PropertySetter.setProperties(layout, props, layoutPrefix + ".");
	  LogLog.debug("End of parsing for \"" + appenderName +"\".");
	}
      }
      PropertySetter.setProperties(appender, props, prefix + ".");
    }
    registryPut(appender);

 上面这个方法,即是解析appender对象,通过log4j.appender.X的前缀(X表示在rootLogger后的appender名称)来取得appender类名,并尝试实例化,然后根据appender来判断是否需要再解析appender的layout(即log4j.appender.X.layout这个键),解析并设置相应属性,最后分别解析appender本身的属性信息和layout的属性信息。(通过ProperSetter这个类,根据javaBean属性映射,将指定后缀后的信息当作一个键,后缀在属性文件中的值作为指定键的值,并将这个键值映射,通过javaBean设置到相应的对象上)

 

    至此,rootLogger即解析完毕。

 

   第二句:解析loggerFactory,略。

   第三句:解析其他logger信息。

  void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
    Enumeration enumeration = props.propertyNames();
    while(enumeration.hasMoreElements()) {
      String key = (String) enumeration.nextElement();
      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());
	}
	String value =  OptionConverter.findAndSubst(key, props);
	Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
	synchronized(logger) {
	  parseCategory(props, logger, key, loggerName, value);
	  parseAdditivityForLogger(props, logger, loggerName);
	}
      } else if(key.startsWith(RENDERER_PREFIX)) {
......
      }
    }
  }

 

这个方法,则主要根据logger.logger为前缀的属性信息进行解析,并根据这个属性信息生成logger,并放在继承树中,设置相应树信息,最后设置其他信息(如appender,level)等,与rootLogger解析基本一致。

 

至此,整个log4j的配置信息已经完成,而这个配置是由JVM保证线程化的(即只能被加载一次),在使用时整个配置已经加载成功,得到的已经是从配置信息中得到的logger对象了。

分享到:
评论
1 楼 gkqzyjsajh 2015-10-29  
写得非常好,很有收获

相关推荐

    tomcat9 slf4j+log4j2 写日志.zip

    总之,配置Tomcat 9使用SLF4J和Log4j2进行日志记录是一个涉及依赖管理、日志配置文件设置和Tomcat自身配置的过程。通过上述步骤,你应该能够成功解决“tomcat9 slf4j+log4j2 不写日志”的问题。如果在实践中遇到问题...

    log4j乱码问题解决办法

    ### log4j乱码问题解决办法 #### 一、引言 在开发过程中,日志记录是必不可少的一个环节。良好的日志系统可以帮助开发者快速定位问题并进行调试。Log4j作为一款广泛使用的Java日志框架,在很多项目中扮演着重要...

    log4j.properties配置详解

    Log4j是一种广泛应用于Java应用程序的日志框架,它可以帮助开发者轻松管理应用程序的日志记录过程。Log4j支持多种配置方式,其中.properties文件配置是最常用的一种。通过.properties文件,我们可以灵活地设置日志...

    [简单]log4jdbc-log4j2配置简记

    标题中的“log4jdbc-log4j2配置简记”指的是在Java开发中使用log4jdbc-log4j2库来监控和记录SQL查询的过程。log4jdbc是一个开源项目,它允许开发者通过日志系统来追踪数据库操作,而log4j2是log4j的升级版,提供了更...

    spring-boot-starter-log4j2

    "spring-boot-starter-log4j2"就是Spring Boot为Log4j2提供的一个启动器,包含了Log4j2运行所需的jar包。当我们添加这个依赖到项目中,Spring Boot会自动配置Log4j2,无需手动进行繁琐的初始化设置。 三、集成步骤 ...

    log4j 介绍

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

    log4j中配置日志文件相对路径方法(PDF)

    ### Log4j中配置日志文件相对路径方法详解 #### 概述 在软件开发过程中,日志记录是一项重要的功能,它有助于开发者调试程序、监控应用程序的运行状态以及追踪问题。`Log4j`作为一款优秀的日志管理工具,被广泛应用...

    log4j配置范例

    log4j配置范例,完全手写会遇到困难吗,这是个例子供大家参考

    java log4j统一打印在user.dir目录下(windows、linux通用、不用考虑不同操作系统分隔符不一致的情况)

    Java日志管理是开发过程中必不可少的部分,而Log4j作为Java世界中广泛使用的日志框架,因其灵活性和可配置性而备受青睐。本教程将详细解释如何配置Log4j,使其在不同操作系统环境下(Windows和Linux)都能统一将日志...

    Log4j记录日志DEMO

    **Log4j记录日志DEMO** 在Java开发中,日志记录是不可或缺的一部分,它帮助开发者追踪程序运行状态、定位错误和调试代码。...在`Log4jDemo`中,你可以看到这些概念的具体应用,进一步了解Log4j的日志记录过程。

    springboot+mybatis+log4j2

    在项目中,"springBootMybatis"这个压缩包文件可能包含了以下内容:Spring Boot的启动类、MyBatis的相关配置(如`mybatis-config.xml`)、Mapper接口及对应的XML映射文件、实体类、服务层接口和实现、以及Log4j2的...

    Log4j配置文件

    3. **通过环境变量传递配置文件**: Log4j会在初始化过程中查找特定的环境变量来加载配置文件。 4. **通过应用服务器配置**: 在部署到应用服务器时,可以通过特定的Servlet来配置Log4j。 #### 六、总结 Log4j的配置...

    maven+springmvc+mybatis+log4j框架搭建

    本教程将详细阐述如何使用四个关键组件——Maven、SpringMVC、MyBatis和Log4j——来搭建一个强大的Web应用框架,旨在提高开发效率并优化项目管理。 **Maven** 是一个流行的项目管理和综合工具,它通过统一的构建...

    log4j使用详解 j2EE

    #### 四、在代码中使用Log4j 要在代码中使用Log4j,首先需要获取一个Logger实例。 - **语法**:`public static Logger getLogger(String name)` - **示例**: ```java import org.apache.log4j.Logger; ...

    log4j配置使用,描述了log4j的配置实用

    ### log4j配置详解 #### 一、log4j简介 Log4j 是一个开源的日志记录工具,它允许开发者自定义日志级别并能够输出丰富的日志信息。本篇文章将详细解读 log4j 的基本配置及使用方法,非常适合初学者理解和掌握。 ##...

    Log4j.properties详细说明

    3. 配置放在文件里,通过环境变量传递文件名等信息,利用 log4j 默认的初始化过程解析并配置。 4. 配置放在文件里,通过应用服务器配置。 Log4j.properties 文件是 Log4j 框架的核心配置文件,用于设置记录器的级别...

    log4j学习

    标题“log4j学习”指出我们将探讨的是日志处理框架Log4j,这是一个广泛使用的Java日志工具,主要用于记录应用程序运行过程中的各种事件和异常信息。描述部分虽然为空,但通常在学习Log4j时,我们会涉及它的基本概念...

    log4j日志管理

    ### Log4j日志管理详解 #### 一、Log4j概述 Log4j是一款流行的开源日志框架,由Apache软件基金会提供。它允许开发者在应用程序中加入日志功能,以便于跟踪程序运行过程中的各种状态信息。Log4j的核心设计理念在于...

    无法打出log4j日志的问题排查

    10. **异常处理**:如果Log4j的初始化过程中出现异常,可能不会有任何日志输出。查看应用启动时的异常堆栈,找出可能的问题。 排查过程中,可以尝试逐步简化配置,只保留基本的控制台输出,以确定问题是否在于复杂...

    log4j jar包下载

    3. **初始化**:在应用程序启动时,Log4j会自动加载配置文件,并根据配置设置日志记录行为。 4. **使用API**:通过调用`Logger`类的方法,如`logger.info()`, `logger.error()`等,可以在代码中插入日志语句。 5. ...

Global site tag (gtag.js) - Google Analytics