自訂log4j的Appender

來源:互聯網
上載者:User

http://is00hcw.blog.163.com/blog/static/282632722010418102837438/

編寫自訂appender 的 步驟

1. 擴充 AppenderSkeleton 抽象類別。如果是通過流方式實現讀寫資料的話,自定一定appender可以從WriterAppender繼承,這樣只需要把我們自己的OutputStream串連到WriterAppender.qw上就可以了。更方便快捷。

2. 指定您的 appender 是否需要 layout。這個由requiresLayout()方法確定。

3. 如果某些屬性必須同時啟用,則應該在 activateOptions() 方法內完成。該方法上在Appender的建構函式之後被調用的。

4. 實現 close() 方法。它必須把 closed 欄位的值設定為 true 。記得釋放所有資源。

5. 可選地指定要使用的預設 ErrorHandler 對象。系統預設為OnlyOnceErrorHandler,它發送出第一個錯誤的訊息並忽略其餘的所有錯誤,錯誤訊息將輸出到 System.err。

6. 編寫 append() 方法的代碼。這個方法負責附加日誌記錄事件,並在錯誤發生時負責調用錯誤處理程式。我們主要的日誌記錄等處理任務實際上是在該append()方法內完成的。

 

請看程式。
由測試程式:Log4jTest.java、
自訂的Appdender:UDPAppender
設定檔:log4j.properties
三個源檔案組成。
測試程式:Log4jTest.java

package zieckey.study.log4j;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Log4jTest {
// 擷取日誌記錄器
static Logger logger = Logger.getLogger(Log4jTest.class.getName());
    Log4jTest() {
// 讀取使用Java屬性檔案編寫的設定檔
logger.debug( "Read config file." );
        PropertyConfigurator.configure("src/log4j.properties");
}
public static void printLog() {
logger.debug("Log4jTest-->>debug");
logger.info("Log4jTest-->>info");
logger.warn("Log4jTest-->>warn");
logger.error("Log4jTest-->>error");
}
public static void main(String[] args) {
        Log4jTest.printLog();
new Log4jTest();
}
}

自訂的Appdender:UDPAppender

4j;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
public class UDPAppender extends AppenderSkeleton {
static private int bufferSize = 8 * 1024;
private byte data[];
private String remoteHost = "localhost";
private int port = 5000;
private InetAddress address = null;
private DatagramSocket dataSocket = null;
private DatagramPacket dataPacket = null;
public UDPAppender() {
// LogLog.setInternalDebugging(true);
// LogLog.setQuietMode(false);
// LogLog.debug("default constructor.");
}
private void init() {
try {
            dataSocket = new DatagramSocket(this.port + 1);
address = InetAddress.getByName(remoteHost);
} catch (SocketException e) {
            LogLog.debug(e.getMessage());
} catch (UnknownHostException e) {
            LogLog.debug(e.getMessage());
}
        data = new byte[bufferSize];
if (this.layout == null) {
            LogLog.debug("The layout is not loaded... we set it.");
String pattern = "%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n";
this.setLayout(new org.apache.log4j.PatternLayout(pattern));
}
}
    @Override
protected void append(LoggingEvent event) {
try {
String msg = "UDP Appender...send data: "
+ this.getLayout().format(event);
            data = msg.getBytes();
            dataPacket = new DatagramPacket(data, data.length, address, port);
            dataSocket.send(dataPacket);
} catch (SocketException se) {
            se.printStackTrace();
} catch (IOException ie) {
            ie.printStackTrace();
}
}
/**
     * Derived appenders should override this method if option structure
     * requires it.
     */
public void activateOptions() {
init();
}
    @Override
public void close() {
if (closed)
return;
if (!dataSocket.isClosed()) {
            dataSocket.close();
}
        closed = true;
}
    @Override
public boolean requiresLayout() {
return true;
}
/**
     * The <b>RemoteHost</b> option takes a string value which should be the
     * host name of the server where a {@link SocketNode} is running.
     * */
public void setRemoteHost(String host) {
String val = host.trim();
        remoteHost = val;
}
/**
     * Returns value of the <b>RemoteHost</b> option.
     */
public String getRemoteHost() {
return remoteHost;
}
/**
     * The <b>Port</b> option takes a positive integer representing the port
     * where the server is waiting for connections.
     */
public void setPort(int port) {
this.port = port;
}
/**
     * Returns value of the <b>Port</b> option.
     */
public int getPort() {
return port;
}
}

設定檔:log4j.properties

log4j.rootLogger=DEBUG,CONSOLE,UDPAppender,LOGFILE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n
log4j.appender.UDPAppender=zieckey.study.log4j.UDPAppender
log4j.appender.UDPAppender.RemoteHost=localhost
log4j.appender.UDPAppender.Port=4561
log4j.appender.UDPAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.UDPAppender.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n
log4j.appender.LOGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.LOGFILE.File=Log4jTest.log
log4j.appender.LOGFILE.MaxFileSize=20KB
log4j.appender.LOGFILE.MaxBackupIndex=1
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n

注意設定檔應該放在src目錄下。

appender 的生命週期

 

 

1. appender 執行個體不存在,或許架構還沒有配置好。

2. 架構執行個體化了一個新的 appender。這發生在配置器類分析配置指令碼中的一個 appender 聲明的時候。配置器類調用 Class.newInstance(YourCustomAppender.class) ,這等價於動態調用 new YourCustomAppender() 。架構這樣做是為了避免被寫入程式碼為任何特定的 appender 名稱;架構是通用的,適用於任何 appender。

e.g. log4j.appender.file=org.apache.log4j.RollingFileAppender

3. 架構判斷 appender 是否需要 layout。如果該 appender 不需要 layout,配置器就不會嘗試從配置指令碼中載入 layout 資訊。

在Appender介面中,有public boolean requiresLayout() 方法,如果return true;則要求layout,需在配置指令碼中設定layout資訊

e.g. log4j.appender.file.layout=org.apache.log4j.PatternLayout

4. Log4j 配置器調用 setter 方法。在所有屬性都已設定好之後,架構就會調用這個方法。

此時對應RollingFileAppender的每個Field,需要有一個setter方法,在配置指令碼中也要進行設定

e.g. log4j.appender.file.File=<project>.log

對應的在RollingFileAppender中,有

public void setFile(String file) {

    String val = file.trim();

    fileName = val;

}

其它的屬性,如MaxFileSize ,也相同

5. 配置器調用 activateOptions() 方法。在所有屬性都已設定好之後,架構就會調用這個方法。

OptionHandler 介面定義了activateOptions()方法,在全部屬性設定好了之後,在該方法中進行必要的操作,如開啟檔案

e.g

if(fileName != null) {

      try {

    setFile(fileName, fileAppend, bufferedIO, bufferSize);

      }

      catch(java.io.IOException e) {

    errorHandler.error("setFile("+fileName+","+fileAppend+") call failed.",

               e, ErrorCode.FILE_OPEN_FAILURE);

      }

    } else {

      //LogLog.error("File option not set for appender ["+name+"].");

      LogLog.warn("File option not set for appender ["+name+"].");

      LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");

}

6. Appender 準備就緒。 此刻,架構可以調用 append() 方法來處理日誌記錄請求。這個方法由 AppenderSkeleton.doAppend() 方法調用。

該方法為Appender中最關鍵的方法,append()中可以定義日誌的輸出,最簡單的:

protected void append(LoggingEvent event){

    //if layout is required

    System.out.println(this.getLayout().format(event));

}

7. 最後,關閉appender。 當架構即將要刪除您的自訂 appender 執行個體時,它會調用您的 appender 的 close() 方法。 close() 是一個清理方法,意味著 您需要釋放已指派的所有資源。它是一個必需的方法,並且不接受任何參數。它必須把 closed 欄位設定為 true ,並在有人嘗試使用關閉的 appender 時向架構發出警報。

public void close() {

        if (this.closed)

            return;

        this.closed = true;

    }

注意: 需要在主程式退出前,調用Logger.getRoot().removeAllAppender();這樣才能調用Appender的close()方法.

 

編寫自訂appender 的 步驟

1. 擴充 AppenderSkeleton 抽象類別。

2. 指定您的 appender 是否需要 layout。

3. 如果某些屬性必須同時啟用,則應該在 activateOptions() 方法內完成。

4. 實現 close() 方法。它必須把 closed 欄位的值設定為 true 。記得釋放所有資源。

5. 可選地指定要使用的預設 ErrorHandler 對象。系統預設為OnlyOnceErrorHandler,它發送出第一個錯誤的訊息並忽略其餘的所有錯誤,錯誤訊息將輸出到 System.err。

6. 編寫 append() 方法的代碼。這個方法負責附加日誌記錄事件,並在錯誤發生時負責調用錯誤處理程式。

 

log4j 環境包括三個主要組件:

  • logger(日誌記錄器):控制要啟用或禁用哪些日誌記錄語句。可以對日誌記錄器指定如下層級: ALL 、 DEBUG 、INFO 、 WARN 、 ERROR , FATA或 OFF 。
  • layout(布局):根據使用者的願望格式化日誌記錄請求。
  • appender:向目的地發送格式化的輸出。

所有的 appender 都必須擴充 org.apache.log4j.AppenderSkeleton 類,這是一個抽象類別,它實現了org.apache.log4j.Appender 和 org.apache.log4j.spi.OptionHandler 介面。 AppenderSkeleton 類的 UML 類圖看起來:

Appender 介面

package org.apache.log4j;public interface Appender {    void addFilter(Filter newFilter);                        void clearFilters() ;                                                   void close();                                                            void doAppend(LoggingEvent event);                ErrorHandler getErrorHandler();         Filter getFilter();             Layout getLayout();              String getName();           boolean requiresLayout();            void setErrorHandler(ErrorHandler errorHandler);            void setLayout(Layout layout);           void setName(String name);         }

這些方法處理 appender 的如下屬性:

  • name:Appender 是命名的實體,因此有一個針對其名稱的 setter/getter。
  • layout: Appender 可以具有關聯的 Layout,因此還有另一個針對 layout 的setter/getter 方法。注意我們說的是“可以”而不是“必須”。這是因為有些 appender 不需要 layout。lauout 管理格式輸出――也就是說,它返回 LoggingEvent的 String 表示形式。另一方面, JMSAppender 發送的事件是 序列化的,因此您不需要對它附加 layout。如果自訂的 appender 不需要 layout,那麼 requiresLayout() 方法必須返回 false ,以避免 log4j 抱怨說丟失了 layout 資訊。
  • errorHandler : 另一個 setter/getter 方法是為 ErrorHandler 而存在的。appender 可能把它們的錯誤處理委託給一個ErrorHandler 對象――即 org.apache.log4j.spi 包中的一個介面。實作類別有兩個:OnlyOnceErrorHandler 和FallbackErrorHandler 。 OnlyOnceErrorHandle 實現 log4j 的預設錯誤處理策略,它發送出第一個錯誤的訊息並忽略其餘的所有錯誤。錯誤訊息將輸出到 System.err 。 FallbackErrorHandler 實現ErrorHandler 介面,以便能夠指定一個輔助的 appender。如果主 appender 失敗,輔助 appender 將接管工作。錯誤訊息將輸出到 System.err ,然後登入到新的輔助 appender。

還有管理過濾器的其他方法(比如 ddFilter() 、 clearFilters() 和 getFilter() 方法 )。儘管 log4j 具有過濾日誌請求的多種內建方法(比如知識庫範圍級、日誌記錄器級和 appender 閾值級),但它使用自訂過濾器方法的能力也是非常強大的。

一個 appender 可以包含多個過濾器。自訂過濾器必須擴充 org.apache.log4j.spi.Filter 抽象類別。這個抽象類別要求把過濾器組織為線性鏈。 對每個過濾器的 decide(LoggingEvent) 方法的調用要按照過濾器被添加到鏈中的順序來進行。自訂過濾器基於三元邏輯。 decide() 方法必須返回 DENY 、 NEUTRAL 或者 ACCEPT 這三個整型常量值之一。

除了 setter/getter 方法以及和過濾器相關的方法外,還有另外兩個方法: close() 和 doAppend() 。 close() 方法釋放 appender 中分配的任何資源,比如檔案控制代碼、網路連接,等等。在編寫自訂 appender 代碼時,務必要實現這個方法,以便當您的 appender 關閉時,它的 closed 欄位將被設定為 true 。

 

  • appender 執行個體不存在。或許架構還沒有配置好。
  • 架構執行個體化了一個新的 appender。這發生在配置器類分析配置指令碼中的一個 appender 聲明的時候。配置器類調用Class.newInstance(YourCustomAppender.class) ,這等價於動態調用 new YourCustomAppender() 。架構這樣做是為了避免被寫入程式碼為任何特定的 appender 名稱;架構是通用的,適用於任何 appender。
  • 架構判斷 appender 是否需要 layout。如果該 appender 不需要 layout,配置器就不會嘗試從配置指令碼中載入 layout 資訊。
  • Log4j 配置器調用 setter 方法。在所有屬性都已設定好之後,架構就會調用這個方法。程式員可以在這裡啟用必須同時啟用的屬性。
  • 配置器調用 activateOptions() 方法。在所有屬性都已設定好之後,架構就會調用這個方法。程式員可以在這裡啟用必須同時啟用的屬性。
  • Appender 準備就緒。 此刻,架構可以調用 append() 方法來處理日誌記錄請求。這個方法由 AppenderSkeleton.doAppend() 方法調用。
  • 最後,關閉appender。 當架構即將要刪除您的自訂 appender 執行個體時,它會調用您的 appender 的 close() 方法。 close() 是一個清理方法,意味著 您需要釋放已指派的所有資源。它是一個必需的方法,並且不接受任何參數。它必須把 closed 欄位設定為 true ,並在有人嘗試使用關閉的 appender 時向架構發出警報。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.