【日誌】log4j.properties 字段如何加載以及如何控制日誌輸出級別(源碼剖析)

一問: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...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章