知ってた? log4jのLoggerとCategoryの違い。

log4jを検索して情報を集めていると、設定ファイルに「log4j.logger.xxx」となっていたり、「log4j.category.xxx」となっていたりして、どっちが正しいのか混乱したので、まとめてみました。

前提

  • 検証したのは、log4j-1.2.17.jar

結論としては

  • 設定ファイルに記述は、loggerとcategoryどっちで書いても同じ。
  • Javaの記述は、Loggerを使う。

以降、ソースを抜粋しながら検証します。

設定ファイルに記述は、loggerとcategoryどっちで書いても同じ。

log4j.properties

# どっちでも同じ
log4j.rootLogger=INFO, A
log4j.logger.xxx=INFO, B

log4j.rootCategory=INFO, A
log4j.category.xxx=INFO, B

org.apache.log4j.PropertyConfigrator.java (Line:102)

  static final String CATEGORY_PREFIX = "log4j.category.";
  static final String LOGGER_PREFIX = "log4j.logger.";

org.apache.log4j.PropertyConfigrator.java (Line:654)

  /**
     Parse non-root elements, such non-root categories and renderers.
  */
  protected
  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);  // loggerでもcategoryでもLoggerを生成している。

Javaの記述は、Loggerを使う。

Loggerクラスは、Categoryクラスを継承している。

org.apache.log4j.Logger.java

/**
  This is the central class in the log4j package. Most logging
  operations, except configuration, are done through this class.

  @since log4j 1.2

  @author Ceki Gülcü */
public class Logger extends Category {

「@since log4j 1.2」とあるので、1.1系ではCategoryクラスを利用していたと推測できます。なので、Categoryクラスで説明されているサイトは1.1系を前提に説明しているのだと思います。

CategoryクラスのgetInstance("xx")は非推奨メソッド

org.apache.log4j.Category.java (Line:516)

 /**
  * @deprecated Make sure to use {@link Logger#getLogger(String)} instead.
  */
  public
  static
  Category getInstance(String name) {
    return LogManager.getLogger(name);
  }

 /**
  * @deprecated Please make sure to use {@link Logger#getLogger(Class)} instead.
  */ 
  public
  static
  Category getInstance(Class clazz) {
    return LogManager.getLogger(clazz);
  }

コメントに Logger#getLoggerを使ってくれって書いてます。

Loggerは、ログレベルTRACEが利用可能

org.apache.log4j.Logger.java (Line:158)

    /**
     * Log a message object with the {@link org.apache.log4j.Level#TRACE TRACE} level.
     *
     * @param message the message object to log.
     * @see #debug(Object) for an explanation of the logic applied.
     * @since 1.2.12
     */
    public void trace(Object message) {
      if (repository.isDisabled(Level.TRACE_INT)) {
        return;
      }

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

    /**
     * Log a message object with the <code>TRACE</code> level including the
     * stack trace of the {@link Throwable}<code>t</code> passed as parameter.
     *
     * <p>
     * See {@link #debug(Object)} form for more detailed information.
     * </p>
     *
     * @param message the message object to log.
     * @param t the exception to log, including its stack trace.
     * @since 1.2.12
     */
    public void trace(Object message, Throwable t) {
      if (repository.isDisabled(Level.TRACE_INT)) {
        return;
      }

      if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel())) {
        forcedLog(FQCN, Level.TRACE, message, t);
      }
    }

このメソッドは、Loggerで実装されているのでCategoryクラスでは利用できません。また、「@since 1.2.12」とあるので1.2.11以前は、traceはなかったのかな。きっと。

まとめ

log4j 1.2.17を見る限りでは、下位互換のためCategoryクラスを継承したLoggerクラスを準備し、Categoryクラスを非推奨にしたと推測できます。これにより、1.1系で利用していた設定ファイルにある「log4j.category.xx」も問題なく動作することができます。

ここで混乱が生じます。
1.2系を利用し、設定ファイル内で「log4j.category.xx」を記述するよう説明されているサイトを参考にしているとします。
すると、Java側ではLoggerクラスのインスタンスを使うため、設定ファイルとLoggerクラスの関連が見えなくなります。さらに、Logger(Category)は階層構造を構築できるため、この階層構造=Categoryの関連性を見出し腑に落ちたように思うのですが、「log4j.logger.xx」の存在が宙に浮きます。

この混乱がないよう上で検証したつもりですので、参考にして下さい。