最初想要在執行一段業務邏輯的時候調用一個外部接口記錄審計信息,一直找不到一個比較優雅的方式,經過討論覺得log4j自定義的appender或許可以實現此功能。后來就了解了一下log4j的這部分。
Apache Log4j 架構
Apache Log4j是當前在J2EE和J2SE開發中用得最多的日志框架(幾乎所有項目都用它),因為它具有出色的性能、靈活的配置以及豐富的功能,並且在業務有特殊的要求時,可以使用自定義組件來代替框架中已有的組件來滿足要求。
log4j組件介紹
Log4j主要有三個組件:
- Logger:負責供客戶端代碼調用,執行debug(Object msg)、info(Object msg)、warn(Object msg)、error(Object msg)等方法。
- Appender:負責日志的輸出,Log4j已經實現了多種不同目標的輸出方式,可以向文件輸出日志、向控制台輸出日志、向Socket輸出日志等。
- Layout:負責日志信息的格式化。
Logger 層級介紹
Logger的層級是logger名字指定的,如x.y 表示兩層,x層和y層,x是y的父層級,x.y所在層級是y層級
log4j.additivity.* = false : 表示當前logger不需要打到父層級所指定的appender,只打到當前的appender;
默認true:表示當前logger將打印日志到當前的appender及所有的父層級所指定的appender 
Layout有多種
最常用且最靈活的輸出格式是: org.apache.log4j.PatternLayout
可以用以下的各項進行組合配置:
- %c logger名字空間的全稱,如果加上{<層數>}表示列出從最內層算起的指定層數的名字空間。
- %C 調用logger的類的全名(包含包路徑)。
- %d 日志記錄時間,{<日期格式>}使用ISO8601定義的日期格式。
- %F 調用logger的源文件名。
- %l 日志事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。
- %L 調用logger的代碼行%m 輸出消息。
- %M 調用logger的方法名。
- %n 當前平台下的換行符。
- %p 該條日志的優先級。
- %r 從程序啟動時到記錄該條日志時已經經過的毫秒數。
- %t 產生該日志事件的線程名。
- %x 按NDC(Nested Diagnostic Context,線程堆棧)順序輸出日志。
- %X 按MDC(Mapped Diagnostic Context,線程映射表)輸出日志。通常用於多個客戶端連接同一台服務器,方便服務器區分是那個客戶端訪問留下來的日志。
- %% 顯示一個百分號。)
執行順序及關系
調用Log4j輸出日志時,調用各個組件的順序:
- 1、日志信息傳入 Logger。
- 2、將日志信息封裝成 LoggingEvent 對象並傳入 Appender。
- 3、在 Appender 中調用 Filter 對日志信息進行過濾,調用 Layout 對日志信息進行格式化,然后輸出。
圖示: 
實現自定義log4j Appender
明白了log4j的結構關系實現自定義的log4j appender就迎刃而解了
繼承log4j公共的基類:AppenderSkeleton
打印日志核心方法:abstract protected void append(LoggingEvent event);
初始化加載資源:public void activateOptions(),默認實現為空
釋放資源:public void close()
是否需要按格式輸出文本:public boolean requiresLayout()
正常情況下我們只需要覆蓋append方法即可。然后就可以在log4j中使用了
demo代碼:
- import org.apache.log4j.AppenderSkeleton;
- import org.apache.log4j.spi.LoggingEvent;
- public class HelloAppender extends AppenderSkeleton {
- private String account ;
- @Override
- protected void append(LoggingEvent event) {
- System.out.println("Hello, " + account + " : "+ event.getMessage());
- }
- @Override
- public void close() {
- // TODO Auto-generated method stub
- }
- @Override
- public boolean requiresLayout() {
- // TODO Auto-generated method stub
- return false;
- }
- public String getAccount() {
- return account;
- }
- public void setAccount(String account) {
- this.account = account;
- }
- }
- public static void main(String[] args) {
- Log log = LogFactory.getLog("helloLog") ;
- log.info("I am ready.") ;
- }
log4j.logger.helloLog=INFO, hello
log4j.appender.hello=HelloAppender
log4j.appender.hello.account=World
執行main函數,輸出結果
Hello, World : I am ready.

