【日志】log4j.properties 字段如何加载以及如何控制日志输出级别(源码剖析)

本文详细解析了Log4j配置文件log4j.properties的加载过程,特别是如何通过配置控制日志输出级别。从demo代码入手,深入探讨log4j.rootLogger配置项的作用,以及它如何影响日志的输出。同时,文章通过源码分析,揭示了日志级别检查的具体实现。

一问:log4j.properties 字段如何加载?

二问:如何控制日志输出级别?


1、先看 demo 代码:

package indi.sword.demo;

import org.apache.log4j.Logger;

/**
 * @author jeb_lin
 * 3:35 PM 28/02/2019
 */
public class Demo {
    private static final Logger LOGGER = Logger.getLogger(Demo.class);

    public static void main(String[] args) {
        LOGGER.debug("abc");
    }
}

2、再看log4j.properties 配置

log4j.rootLogger=info,stdout,rollingLog

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%c{1}#%M:%L) %t - %m%n
log4j.appender.stdout.Threshold=debug

## rolling log file
log4j.appender.rollingLog.File=/Users/Documents/temp/logs/rolling.log
log4j.appender.rollingLog.MaxFileSize=512MB
log4j.appender.rollingLog.MaxBackupIndex=12
log4j.appender.rollingLog.Threshold=debug
log4j.appender.rollingLog.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingLog.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%c{1}#%M:%L) %t - %m%n
log4j.appender.rollingLog=org.apache.log4j.RollingFileAppender

3、提问:为什么我以下设置 ,就可以控制整体的日志级别,源码怎么看?

log4j.rootLogger=info,stdout,rollingLog

log4j.appender.stdout.Threshold=debug
log4j.appender.rollingLog.Threshold=debug

LOGGER.debug("abc");无法打印。只能把下面的info 改成 debug 才能打印。为什么?
log4j.rootLogger=info,stdout,rollingLog

一、学习方法很重要,调试技巧很重要。

1、直接在入口加断点,进入方法
在这里插入图片描述

2、根据debug跳转,直接看代码
在这里插入图片描述

public class Category implements AppenderAttachable {
...
  public void debug(Object message) {
    if(repository.isDisabled(Level.DEBUG_INT))
      return;
    if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
      forcedLog(FQCN, Level.DEBUG, message, null);
    }
  }
...
}

3、直接看关键代码

if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
      forcedLog(FQCN, Level.DEBUG, message, null);
    }

这一段代码表示如果当前的代码 LOGGER.debug(“abc”); 中的 debug 级别大于等于 this.getEffectiveLevel(),那么就打印。

4、好了,看到这,就可以知道this.getEffectiveLevel()就是log4j.properties中log4j.rootLogger=info 这个info值。

5、接下来肯定产生疑问,怎么跑到 Category 这个类来的,还有就是 this.getEffectiveLevel() 这个是啥。

二、this.getEffectiveLevel() 哪来的?

1、 剖析 this.getEffectiveLevel() ,探索下这个Level 是怎么设置进去的,有get自然有set,那么自然可以找到当前类内部的 setLevel 方法

public class Category implements AppenderAttachable {

 volatile protected Level level;
 
...
  public void debug(Object message) {
    if(repository.isDisabled(Level.DEBUG_INT))
      return;
    if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
      forcedLog(FQCN, Level.DEBUG, message, null);
    }
  }
...
 
public Level getEffectiveLevel() {
    for(Category c = this; c != null; c=c.parent) {
      if(c.level != null)
	return c.level;
    }
    return null; // If reached will cause an NullPointerException.
}
...
public void setLevel(Level level) {
    this.level = level;
}

2、 接下来就是 要看 setLevel 在哪个地方被使用到,通过idea的工具,往上层找引用,你会发现不止一个引用处,这样不利于我们排查问题。
在这里插入图片描述

三、setLevel 到底是在那里被引用的?

一般来说,就刚刚我那两段主代码,自然可以想到是 Logger 初始化的时候 setLevel 进去的,看我的上一篇文章=点我=,可以知道 LogManager 有个static 代码块,用于初始化log4j的配置信息。

1、调试技巧 :在 static 的第一段代码打一个断点,不断追踪下去。
在这里插入图片描述
2、跟上一篇文章一样,看到关键代码:selectAndConfigure
在这里插入图片描述
3、继续点进去,看到关键代码 doConfigure
在这里插入图片描述
4、就继续点进去会来到 PropertyConfigurator类 的 doConfigure方法。为什么是PropertyConfigurator 看我上一篇文章=点我=
在这里插入图片描述
5、继续点进去 doConfigure 方法(重点观察红色箭头的两方法,第一个方法是以log4j.threshold 方式设置全局日志Level,第二种是我要讲的log4j.rootLogger=info,stdout,rollingLog 这个info的引用)
在这里插入图片描述
6、 直接看configureRootCategory(properties, hierarchy);为什么是 configureRootCategory 看我上一篇文章=点我=
在这里插入图片描述
7、debug便于理解,直接盯住value这个值
在这里插入图片描述
8、点开 parseCategory 这个方法,看到调试结果没,这就是我们一路找的 setLevel的方法所在地,所以圆满对接上了。
在这里插入图片描述

四、log4j.properties 的其他属性呢?在哪设置进去的?

我们知道 :
log4j.rootLogger = [ level ] , appenderName1, appenderName2, …(默认输出目的地,当前端传入类名)
下面的代码刚好解释了一点 appenderName1日志输出地
在这里插入图片描述
根据log4j.properties 配置文件,配置 stout 与 rollingLog 的 appender,然后logger.addAppender(appender); 完成配置。
在这里插入图片描述

=注意这里的 aai,待会下面有用到=

在这里插入图片描述

于是:
在这里插入图片描述
在这里插入图片描述
info的值>debug,自然不会 ForceLog了。

五、正常如何打印?

1、修改下面的info为debug。

log4j.rootLogger=info,stdout,rollingLog

改为:

log4j.rootLogger=debug,stdout,rollingLog

这下子就进来了
在这里插入图片描述
2、点开 forceLog 方法
在这里插入图片描述
3、 点开callAppenders 方法(注意aai,是不是很熟悉)
在这里插入图片描述
4、进入方法 appendLoopOnAppenders
在这里插入图片描述
5、 debug进入 doAppend 方法,进入到

public abstract class AppenderSkeleton implements Appender, OptionHandler {
  public
  synchronized 
  void doAppend(LoggingEvent event) {
    if(closed) {
      LogLog.error("Attempted to append to closed appender named ["+name+"].");
      return;
    }
    
    if(!isAsSevereAsThreshold(event.getLevel())) {
      return;
    }

    Filter f = this.headFilter;
    
    FILTER_LOOP:
    while(f != null) {
      switch(f.decide(event)) {
      case Filter.DENY: return;
      case Filter.ACCEPT: break FILTER_LOOP;
      case Filter.NEUTRAL: f = f.getNext();
      }
    }
    
    this.append(event);    
  }
}

在这里插入图片描述

6、debug 进入 append方法

public class WriterAppender extends AppenderSkeleton {
  public
  void append(LoggingEvent event) {

    // Reminder: the nesting of calls is:
    //
    //    doAppend()
    //      - check threshold
    //      - filter
    //      - append();
    //        - checkEntryConditions();
    //        - subAppend();

    if(!checkEntryConditions()) {
      return;
    }
    subAppend(event);
   }
}

在这里插入图片描述
7、进入 subAppend方法,看到真正的写操作了
在这里插入图片描述

8、一个是写文件FileAppender、一个是写控制台 stout


OVER...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值