第 13 章:从 log4j 迁移
**变化越多,它们保持不变的程度就越多.
—ALPHONSE KARR,* LesGuêpes***
本章涉及将自定义 log4j 组件(例如追加器或布局)迁移到 logback-classic 的主题。
仅调用 log4jClient 端 API(即org.apache.log4j
包中的Logger
或Category
类)的软件即可通过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
()方法,即PropertyConfigurator
或DOMConfigurator
。因此,布局将有机会检查其选项是否一致,如果是,则 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 实现,对于此琐碎的附加程序而言,该实现就足够了。