第十三章:從 log4j 遷移
本章涉及到的內(nèi)容為將 log4j 的組件,例如 appender 或者 layout 遷移到 logback-classic。
僅僅調(diào)用 log4j 客戶端 API 的軟件,也就是 org.apache.log4j 包中 Logger 或者 Category 類,可以通過 SLF4J 遷移工具使用 SLF4J 來(lái)進(jìn)行自動(dòng)遷移。為了將 log4j.property 文件轉(zhuǎn)換為同等的 logback 配置,你可以使用 log4j.properties 轉(zhuǎn)換器。
在某種程度上來(lái)說,log4j 與 logback-classic 密切相關(guān)。核心組件,logger,appender 以及 layout 在兩個(gè)框架中都存在,并且目的一致。類似的,最重要的內(nèi)部數(shù)據(jù)結(jié)構(gòu),叫做 LoggingEvent,在兩個(gè)框架中非常相似,但是實(shí)現(xiàn)完全不同。最主要的是,在 logback-classic 中,LoggingEvent 實(shí)現(xiàn)了 ILoggingEvent 接口。遷移 log4j 組件到 logback-classic 最大的改變?cè)谟?LoggingEvent 類相關(guān)的實(shí)現(xiàn)不同。但是,請(qǐng)放心,這些變化是有限的。如果你盡了最大的努力仍然不能將 log4j 組件遷移到 logback-classic,你可以通過 logback 開發(fā)郵件列表來(lái)進(jìn)行提問。logback 的開發(fā)者應(yīng)該可以提供指導(dǎo)。
遷移 log4j 的 layout
假設(shè)我們現(xiàn)在要遷移一個(gè)簡(jiǎn)單的,名叫 TrivialLog4jLayout 的 log4j layout,它將日志事件中的消息作為格式化消息返回。代碼如下:
package chapters.migrationFromLog4j;
import org.apache.log4j.Layout;
import org.apache.log4j.spi.LoggingEvent;
public class TrivialLog4jLayout extends Layout {
public void activateOptions() {
}
public String format(LoggingEvent loggingEvent) {
return loggingEvent.getRenderedMessage();
}
public boolean ignoresThrowable() {
return true;
}
}
等價(jià)的 logback-classic TrivialLogbackLayout 如下:
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-classic layout 中,格式化的方法叫做 doLayout,而在 log4j 中叫 format()。因?yàn)樵?logback-classic 中沒有等價(jià)的方法,所以 ignoresThrowable() 方法則不需要。logback-classic layout 必須繼承 LayoutBase<ILoggingEvent> 類。
activateOptions() 方法的優(yōu)點(diǎn)值得進(jìn)一步討論。在 log4j 中,一個(gè) layout 有它自己的 activateOptions() 方法,通過 log4j 的配置程序,也就是 PropertyConfigurator 與 DOMConfigurator,會(huì)在 layout 所有的選項(xiàng)都設(shè)置完之后調(diào)用。因此,layout 有機(jī)會(huì)去檢查它的所有的選項(xiàng)是否一致,如果是,那么開始進(jìn)行初始化。
在 logback-classic 中,layout 必須實(shí)現(xiàn) LifeCycle 接口,該接口包含了一個(gè) start() 方法。這個(gè) start() 方法相當(dāng) log4j 中的 activateOptions() 方法。
遷移 log4j 的 appender
遷移 appender 與遷移 layout 相當(dāng)?shù)念愃?。下面是有一個(gè)名為 TrivialLog4jAppender 的簡(jiǎn)單 appender,它會(huì)在控制臺(tái)輸出由它的 layout 返回的字符串。
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;
}
}
在 logback-classic 中等價(jià)的寫法為 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) {
// AppenderBase.doAppend 只會(huì)在這個(gè) appender 成功啟動(dòng)之后調(diào)用這個(gè)方法
String s = this.layout.doLayout(loggingevent);
System.out.println(s);
}
}
比較這兩個(gè)類,你會(huì)發(fā)現(xiàn) append() 方法的內(nèi)容沒有改變。requiresLayout 方法在 logback 中沒有用到,所以它可以被移除。在 logback 中,stop() 方法與 log4j 中的 close() 方法等價(jià)。然而,logback-classic 中的 AppenderBase 包含一個(gè)沒有實(shí)現(xiàn)的 stop 方法,但是在這個(gè)簡(jiǎn)單的 appender 已經(jīng)足夠了。