1. 程式人生 > >log4j2 不使用配置檔案,動態生成logger物件

log4j2 不使用配置檔案,動態生成logger物件

大家平時使用Log4j一般都是在classpath下放置一個log4j的配置檔案,比如log4j.xml,裡面配置好Appenders和Loggers,但是前一陣想做某需求的時候,想要的效果是每一個任務都要有一個單獨的日誌檔案記錄下來,比如job.001.log,job.002.log這種,這種完全不能使用配置檔案來設定。

整體架構:

Log4j的組成:

  Log4j由三個重要的組成構成:日誌記錄器(Loggers),輸出端(Appenders)和日誌格式化器(Layout)。

1.日誌記錄器(Loggers):控制要輸出哪些日誌記錄語句,對日誌資訊進行級別限制。
2.輸出端(Appenders):指定了日誌將列印到控制檯還是檔案中。

3.日誌格式化器(Layout):控制日誌資訊的顯示格式。

  於是點開Log4j的原始碼,研究了一番,發現使用Log4j的底層程式碼完全可以動態生成新的Logger,比較關鍵的類有

org.apache.logging.log4j.core.Layout負責格式
org.apache.logging.log4j.core.Appender就是配置檔案裡的Appender,我們使用FileAppender,也可以使用RollingFileAppender,
org.apache.logging.log4j.core.config.Configuration和org.apache.logging.log4j.core.LoggerContext是負責Log4j的配置的。

LoggerContext

  在日誌系統中,LoggerContext扮演者重要的角色。然而,根據實際情況,一個應用中可能存在多個有效的LoggerContext。

Configuration  

  每一個LoggerContext都有一個有效的Configuaration,它包含了所有的Appenders、context-wide Filters、LoggerConfigs以及對StrSubstitutor的引用。在重新配置期間,兩個Configuaration會同時存在;一旦日誌器被重新賦予新的Configuaration,舊的Configuaration就會停止工作並丟棄。

LoggerConfig

  LoggerConfig將會在Loggers在logging configuration中被宣告的時候建立。在LoggerConfig 擁有一列類的過濾器(Filter),這些過濾器將會過來所有的記錄日誌的事件,只有符合要求的日誌才會被傳遞到Appenders。因為LoggerConfig需要將事件傳遞給Appenders,所以它擁有一系列Appenders的引用。

Appender

   根據logger請求選擇去接受或者拒絕該只是他們的一個能力。Log4j允許日誌列印服務列印到多個目的地上。在Log4j的說法是一個輸出的目的地對應著一個Appdender。現在的Appender允許是console, files, remote socket servers, Apache Flume, JMS, remote UNIX Syslog daemons, and various database APIs。

  是在Log4j2.3下測試通過的。注意在不使用Logger的時候一定要呼叫stop方法將Logger移除,不然我自己測試生成7000多個Logger後便會報錯open too many file。

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 
 */
public class JobLogFactory {
  private JobLogFactory() {
  }
  public static void start(int jobId) {
   //為false時,返回多個LoggerContext物件,   true:返回唯一的單例LoggerContext
   final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();
   //建立一個展示的樣式:PatternLayout,   還有其他的日誌列印樣式。
    Layout layout = PatternLayout.createLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN, 
        config, null, null, true, false, null, null);
    //TriggeringPolicy tp = SizeBasedTriggeringPolicy.createPolicy("10MB");
    //Appender appender = RollingFileAppender.createAppender(String.format("
    // logs/test/syncshows-job-%s.log", jobID),
     //       "/logs/test/" + jobID + "/syncshows-job-" + jobID + ".log.gz",
     //       "true", jobID, null, null, null, tp, null, layout, null,
     //       null, null, null, config);
   //  日誌列印方式——輸出為檔案
   Appender appender = FileAppender.createAppender(
        String.format("logs/test/syncshows-job-%s.log", jobId), "true", "false", 
        "" + jobId, null, "true", "true", null, layout, null, null, null, config);
    appender.start();
    config.addAppender(appender);
   AppenderRef ref = AppenderRef.createAppenderRef("" + jobId, null, null);
    AppenderRef[] refs = new AppenderRef[]{ref};
    LoggerConfig loggerConfig = LoggerConfig.createLogger("false", Level.ALL, "" + jobId,
        "true", refs, null, config, null);
    loggerConfig.addAppender(appender, null, null);
    config.addLogger("" + jobId, loggerConfig);
    ctx.updateLoggers();
  }
  public static void stop(int jobId) {
    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();
    config.getAppender("" + jobId).stop();
    config.getLoggerConfig("" + jobId).removeAppender("" + jobId);
    config.removeLogger("" + jobId);
    ctx.updateLoggers();
  }
  /**
   * 獲取Logger
   *
   * 如果不想使用slf4j,那這裡改成直接返回Log4j的Logger即可
   * @param jobId
   * @return
   */
  public static Logger createLogger(int jobId) {
    start(jobId);
    return LoggerFactory.getLogger("" + jobId);
  }
}

測試類:

import org.slf4j.Logger;
/**
 * 
simple test
 */
public class LoggerTest {
  public static void main(String[] args) {
    for(int i=0;i<50000;i++) {
      Logger logger = JobLogFactory.createLogger(i);
      logger.info("Testing testing testing 111");
      logger.debug("Testing testing testing 222");
      logger.error("Testing testing testing 333");
      JobLogFactory.stop(i);
    }
  }
}

  致謝,感謝您的閱讀!