第 13 章:从 log4j 迁移

**变化越多,它们保持不变的程度就越多.

—ALPHONSE KARR,* LesGuêpes***

本章涉及将自定义 log4j 组件(例如追加器或布局)迁移到 logback-classic 的主题。

仅调用 log4jClient 端 API(即org.apache.log4j包中的LoggerCategory类)的软件即可通过SLF4J 迁移器工具自动迁移为使用 SLF4J。要将* log4j.property *文件迁移到等效的 logback 中,可以使用log4j.properties translator

从更广泛的角度来看,log4j 和经典的 logback 密切相关。Logger,附加器和布局之类的核心组件都存在于两个框架中,并且具有相同的用途。同样,最重要的内部数据结构LoggingEvent存在于两个框架中,它们的实现方式非常相似,但不完全相同。最值得注意的是,在经典的 logback 中,LoggingEvent实现了ILoggingEvent接口。将 log4j 组件迁移到 logback-classic 所需的大多数更改与LoggingEvent类的实现差异有关。放心,这些差异相当有限。如果尽最大努力仍无法将任何给定的 log4j 组件迁移到经典的 logback,请在logback-dev 邮件列表上发布问题。logback开发人员应该能够提供指导。

迁移 log4j 布局

让我们开始迁移一个名为TrivialLog4jLayout的假想且简单的 log4j 布局,该布局将记录事件中包含的消息作为格式化的消息返回。这是代码。

package chapters.migrationFromLog4j;

import org.apache.log4j.Layout;
import org.apache.log4j.spi.LoggingEvent;

public class TrivialLog4jLayout extends Layout {

  public void activateOptions() {
    // there are no options to activate
  }

  public String format(LoggingEvent loggingEvent) {
    return loggingEvent.getRenderedMessage();
  }

  public boolean ignoresThrowable() {
    return true;
  }
}

经典的等效于TrivialLogbackLayout的 logback-class 将是

package chapters.migrationFromLog4j;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class TrivialLogbackLayout extends LayoutBase<ILoggingEvent> {

  public String doLayout(ILoggingEvent loggingEvent) {
    return loggingEvent.getMessage();
  }
}

如您所见,在经典的 logback 布局中,格式化方法在 log4j 中名为doLayout而不是format()。不需要ignoresThrowable()方法,并且在 logback-classic 中没有等效的方法。请注意,经典的 logback 布局必须扩展LayoutBase<ILoggingEvent>类。

activateOptions()方法值得进一步讨论。在 log4j 中,在设置了布局的所有选项之后,布局将由 log4j 配置程序调用其activateOptions()方法,即PropertyConfiguratorDOMConfigurator。因此,布局将有机会检查其选项是否一致,如果是,则 continue 进行完全初始化。

在经典的 logback 模式中,布局必须实现LifeCycle接口,该接口包含名为start()的方法。 start()方法等效于 log4j 的activateOptions()方法。

迁移 log4j 附加程序

迁移附加程序与迁移布局非常相似。这是一个称为TrivialLog4jAppender的简单附加程序,它在控制台上写入其布局返回的字符串。

package chapters.migrationFromLog4j;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;

public class TrivialLog4jAppender extends AppenderSkeleton {

  protected void append(LoggingEvent loggingevent) {
    String s = this.layout.format(loggingevent);
    System.out.println(s);
  }

  public void close() {
    // nothing to do
  }

  public boolean requiresLayout() {
    return true;
  }
}

名为TrivialLogbackAppender的经典后备日志将写为

package chapters.migrationFromLog4j;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;

public class TrivialLogbackAppender extends AppenderBase<ILoggingEvent> {

  @Override
  public void start() {
    if (this.layout == null) {
      addError("No layout set for the appender named [" + name + "].");
      return;
    }
    super.start();
  }

  @Override
  protected void append(ILoggingEvent loggingevent) {
    // note that AppenderBase.doAppend will invoke this method only if
    // this appender was successfully started.
    
    String s = this.layout.doLayout(loggingevent);
    System.out.println(s);
  }
}

比较这两个类,您应该注意到append()方法的内容保持不变。 requiresLayout方法未在logback中使用,可以删除。在 logback 中,stop()方法等效于 log4j 的close()方法。但是,logback-classic 中的AppenderBase包含stop的 nop 实现,对于此琐碎的附加程序而言,该实现就足够了。