JAVA日志--log4j(源码分析)

日志级别

log4j有6个日志级别:fatal(严重)-->error(错误)-->warn(警告)-->info(运行信息)-->debug(调试)-->trace(追踪)

日志级别从左到右依次递减

一、不使用配置文件

步骤:首先通过BasicConfigurator.configure()初始化默认的配置信息,接着创建日志记录器,最后输出日志。和JUL日志的类似

代码:

public void logTest() {
		//初始化配置信息
		BasicConfigurator.configure();
		
		//创建日志记录器
		Logger logger = Logger.getLogger(Log4jTest.class);
		
		//输出日志
		logger.fatal("fatal:严重错误");		//一般会造成系统崩溃
		logger.error("error:错误信息");		//不会影响系统运行
		logger.warn("warn:警告信息");			//可能会发生问题
		logger.info("info:运行信息");			//数据连接,网络连接,io操作等
		logger.debug("debug:调试信息");		//调试过程信息 (默认级别)
		logger.trace("trace:追踪信息");		//记录程序的流程信息
		
	}

输出结果:

0 [main] FATAL forTest.Log4jTest  - fatal:严重错误
0 [main] ERROR forTest.Log4jTest  - error:错误信息
0 [main] WARN forTest.Log4jTest  - warn:警告信息
0 [main] INFO forTest.Log4jTest  - info:运行信息
0 [main] DEBUG forTest.Log4jTest  - debug:调试信息

通过BasicConfigurator.configure()我们查看源代码找到configure方法

 /**
     Add a {@link ConsoleAppender} that uses {@link PatternLayout}
     using the {@link PatternLayout#TTCC_CONVERSION_PATTERN} and
     prints to <code>System.out</code> to the root category.  */
  static
  public
  void configure() {
    Logger root = Logger.getRootLogger();
    root.addAppender(new ConsoleAppender(
           new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));
  }

1、可以发现在初始化的时候是创建了一个RootLogger的顶级父元素logger对象,同时添加了一个Appender对象,这是用来定义输出方式的。这里的ConsoleAppender是指定控制台输出,并且通过PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)设置了输出格式。

默认的日志输出级别为debug

二、使用配置文件

程序代码:

public void logQuickTest() {
		
		//开启log4j内置日志
		LogLog.setInternalDebugging(true);
		
		
		//创建日志记录器
		Logger logger = Logger.getLogger(Log4jTest.class);
		/*	输出日志文件测试
		for(int i=0;i<10000;i++) {
			//输出日志
			logger.fatal("fatal:严重错误");		//一般会造成系统崩溃
			logger.error("error:错误信息");		//不会影响系统运行
			logger.warn("warn:警告信息");			//可能会发生问题
			logger.info("info:运行信息");			//数据连接,网络连接,io操作等
			logger.debug("debug:调试信息");		//调试过程信息 (默认级别)
			logger.trace("trace:追踪信息");		//记录程序的流程信息
		}*/
		
		//输出日志
		logger.fatal("fatal:严重错误");		//一般会造成系统崩溃
		logger.error("error:错误信息");		//不会影响系统运行
		logger.warn("warn:警告信息");			//可能会发生问题
		logger.info("info:运行信息");			//数据连接,网络连接,io操作等
		logger.debug("debug:调试信息");		//调试过程信息 (默认级别)
		logger.trace("trace:追踪信息");		//记录程序的流程信息		
	}

这里并没有使用 BasicConfigurator.configure() 初始化,原理我们通过源码查找,这里先跳过log4j内置日志的讲解,最后再来看,直接看到Logger logger = Logger.getLogger(Log4jTest.class)

进入到getLogger方法中

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

 

我们发现了返回对象是LogManager.getLogger(clazz.getName()),说明很有可能和LogManager类有关系,直接进入LogManager类中,查看LogManager类

在其的静态属性中发现了默认的文件名属性,并且DEFAULT_CONFIGURATION_FILE官方注明是过时的了,他们推荐的使用log4j.xml。不过这里我们还是使用log4j.properties

 static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
  
  static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";

也就是log4j.properties和log4j.xml

然后找到下面的静态代码块

static {
    // By default we use a DefaultRepositorySelector which always returns 'h'.
    Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
    repositorySelector = new DefaultRepositorySelector(h);

    /** Search for the properties file log4j.properties in the CLASSPATH.  */
    String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
						       null);

    // if there is no default init override, then get the resource
    // specified by the user or the default config file.
    if(override == null || "false".equalsIgnoreCase(override)) {

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

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

      URL url = null;

      // if the user has not specified the log4j.configuration
      // property, we search first for the file "log4j.xml" and then
      // "log4j.properties"
      if(configurationOptionStr == null) {	
	url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
	if(url == null) {
	  url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
	}
      } else {
	try {
	  url = new URL(configurationOptionStr);
	} catch (MalformedURLException ex) {
	  // so, resource is not a URL:
	  // attempt to get the resource from the class path
	  url = Loader.getResource(configurationOptionStr); 
	}	
      }
      
      // If we have a non-null url, then delegate the rest of the
      // configuration to the OptionConverter.selectAndConfigure
      // method.
      if(url != null) {
	    LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
        try {
            OptionConverter.selectAndConfigure(url, configuratorClassName,
					   LogManager.getLoggerRepository());
        } catch (NoClassDefFoundError e) {
            LogLog.warn("Error during default initialization", e);
        }
      } else {
	    LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
      }
    } else {
        LogLog.debug("Default initialization of overridden by " + 
            DEFAULT_INIT_OVERRIDE_KEY + "property."); 
    }  
  } 

在上面的源代码中,我们可以看到

 /** Search for the properties file log4j.properties in the CLASSPATH.  */
    String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
						       null);

通过注释我们可以理解,从这里开始寻找配置文件,紧接着下面是一些判断,再接着出现下面的语句:

如果我们没有配置log4j.configuration,我们首先会查找log4j.xml,然后再查找log4j.properties,也就是我们自定义的配置文件。

并通过加载器获取路径 url = Loader.getResource(DEFAULT_CONFIGURATION_FILE)

// if the user has not specified the log4j.configuration
      // property, we search first for the file "log4j.xml" and then
      // "log4j.properties"
      if(configurationOptionStr == null) {	
	url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
	if(url == null) {
	  url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
	}
      } else {
	try {
	  url = new URL(configurationOptionStr);
	} catch (MalformedURLException ex) {
	  // so, resource is not a URL:
	  // attempt to get the resource from the class path
	  url = Loader.getResource(configurationOptionStr); 
	}	
      }

到这里,就知道了log4j初始化配置文件的原理了。如果我们没有配置文件,我们要通过BasicConfigurator.configure() 初始化,在BasicConfigurator.configure()中创建了一个RootLogger的顶级父元素logger对象,同时添加了一个Appender对象,如果我们有配置文件,log4j就会进行查找,然后就可以加载到我们配置的logger和appender了。

下面是配置文件的详细配置

# 顶级父元素配置console、file、rollingfile、dailyfile
log4j.rootLogger = trace,console,file

# 自定义logger,级别覆盖,logger继承
log4j.logger.forTest.Log4jTest = info,rollingfile


# 控制台输出配置
log4j.appender.console = org.apache.log4j.ConsoleAppender
# log4j.appender.console.layout = org.apache.log4j.SimpleLayout
# 配置layout格式
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 配置Pattern属性conversionPattern
log4j.appender.console.layout.conversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS}[%p]%r %l %m%n



# 文件输出配置 FileAppender
log4j.appender.file = org.apache.log4j.FileAppender

# 配置layout格式类型
log4j.appender.file.layout = org.apache.log4j.PatternLayout
# 配置Pattern属性conversionPattern指定输出格式
log4j.appender.file.layout.conversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS}[%p]%r %l %m%n
# 配置文件保存路径
log4j.appender.file.file = H:/test/forTest/log4jn.log
# 指定日志文件内容的字符集
log4j.appender.file.encoding = UTF-8




# RollingFileAppender
# 按照文件大小拆分日志文件
log4j.appender.rollingfile = org.apache.log4j.RollingFileAppender

# 配置layout格式类型
log4j.appender.rollingfile.layout = org.apache.log4j.PatternLayout
# 配置Pattern属性conversionPattern指定输出格式
log4j.appender.rollingfile.layout.conversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS}[%p]%r %l %m%n
# 配置文件保存路径
log4j.appender.rollingfile.file = H:/test/forTest/log4j.log
# 指定日志文件内容的字符集
log4j.appender.rollingfile.encoding = UTF-8
# 指定允许的单个日志文件大小
log4j.appender.rollingfile.maxFileSize = 1MB
# 指定最多的日志文件数目,超过则覆盖存在最久的日志信息
log4j.appender.rollingfile.maxBackupIndex = 10




# DailyRollingFileAppender
# 按照时间拆分日志文件
log4j.appender.dailyfile = org.apache.log4j.DailyRollingFileAppender

# 配置layout格式类型
log4j.appender.dailyfile.layout = org.apache.log4j.PatternLayout
# 配置Pattern属性conversionPattern指定输出格式
log4j.appender.dailyfile.layout.conversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS}[%p]%r %l %m%n
# 配置文件保存路径
log4j.appender.dailyfile.file = H:/test/forTest/log4j.log
# 指定日志文件内容的字符集
log4j.appender.dailyfile.encoding = UTF-8
# 指定拆分单位
log4j.appender.dailyfile.datePattern = '.'yyyy-MM-dd-HH-mm-ss



log4j.appender.console.layout.conversionPattern中的字母表示:

# %m	输出代码中指定的日志信息
# %p	日志级别
# %n	换行符
# %r	输出日志的时间,单位毫秒
# %c	语句所属的类名
# %t	输出此日志信息的线程名
# %d	当前服务器时间
# %l	当前日志发生位置包括:类名、线程、所在代码行数
# %F	日志消息产生时所在的文件名
# %L	输出代码的行号
# %%	输出%
# %5c   在%和字符之间加上数字控制字符长度

配置文件中的相应属性都可以在相关的appender中查看,例如RollingFileAppender我们可以在源代码中找到所有属性

 /**
     The default maximum file size is 10MB.
  */
  protected long maxFileSize = 10*1024*1024;

  /**
     There is one backup file by default.
   */
  protected int  maxBackupIndex  = 1;

  private long nextRollover = 0;

其他的类似。他们之间的继承关系也能在源代码中查看。

最后来看log4j内置日志:LogLog.setInternalDebugging(true)默认是关闭的,将属性设为true即可下面是内置日志的输出:

log4j: Trying to find [log4j.xml] using context classloader sun.misc.Launcher$AppClassLoader@14ae5a5.
log4j: Trying to find [log4j.xml] using sun.misc.Launcher$AppClassLoader@14ae5a5 class loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader sun.misc.Launcher$AppClassLoader@14ae5a5.
log4j: Using URL [file:/H:/test/forTest/bin/log4j.properties] for automatic log4j configuration.
log4j: Reading configuration from URL file:/H:/test/forTest/bin/log4j.properties
log4j: Parsing for [root] with value=[trace,console].
log4j: Level token is [trace].
log4j: Category root set to TRACE
log4j: Parsing appender named "console".
log4j: Parsing layout options for "console".
log4j: Setting property [conversionPattern] to [%d{yyyy-MM-dd HH:mm:ss.SSS}[%p]%r %l %m%n].
log4j: End of parsing for "console".
log4j: Parsed "console" options.
log4j: Parsing for [forTest.Log4jTest] with value=[info,file].
log4j: Level token is [info].
log4j: Category forTest.Log4jTest set to INFO
log4j: Parsing appender named "file".
log4j: Parsing layout options for "file".
log4j: Setting property [conversionPattern] to [%d{yyyy-MM-dd HH:mm:ss.SSS}[%p]%r %l %m%n].
log4j: End of parsing for "file".
log4j: Setting property [file] to [H:/test/forTest/log4jn.log].
log4j: Setting property [encoding] to [UTF-8].
log4j: setFile called: H:/test/forTest/log4jn.log, true
log4j: setFile ended
log4j: Parsed "file" options.
log4j: Handling log4j.additivity.forTest.Log4jTest=[null]
log4j: Finished configuring.

从上面的输出我们可以知道,内置日志就是log4j从创建日志记录器开始的执行步骤包括配置文件的查找与读取,配置信息的执行,与我们上面讲的大致相同,通过内置日志我们可以清楚的了解到log4j的执行过程,便于我们分析,当过程出错时可以清晰地找到问题所在。

注意:在自定义logger中,级别是覆盖顶级父元素的级别的,但是appender是继承的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值