log4j學習之SocketAppender


    前面這幾個Appender就是Log4J提供的基於文件系統的Appender。當然,在一些特殊的需要記錄到文件的Appender來說,我們只需要選擇一個合適的Appender來繼承並完成自己的邏輯即可。關於自定義Appender,待會再看。下面來看幾個比較特殊的Appender。


    前面所有介紹的Appender都有一個共同的特點,就是他們都需要配置一個Layout對象,下面要介紹的這個Appender非常特殊,他本身不需要任何Layout對象,這個Appender就是SocketAppender。注意,要能理解SocketAppender,必須對java網絡編程和java對象序列化有相關了解。
    顧名思義SocketAppender是基於Socket的Appender,他不會對日誌消息做任何的處理,而僅僅是通過Socket將日誌請求發送到特定的服務器上。既然是把日誌請求發送到特定的服務器上,首先要考慮的就是日誌消息使用什麼樣的方式發送,在SocketAppender中,只是簡單的把LoggingEvent對象直接通過序列化的方式發送到Socket的輸出流上。所以,要能正常使用SocketAppender,就不單單是使用日誌的應用了,還需要一個能統一和接受日誌消息的獨立的日誌服務器應用了。我們下面就簡單的把使用日誌功能的應用稱爲Logging Client,把接收和處理日誌消息的服務器應用稱爲Logging Server。
    要配置Logging Client端的appender這個已經不是難點了,下面的問題是我們需要自己來寫一個logging server來接收我們的日誌消息。要完成這樣一個服務器端的應用,首先我們要確定幾個需求。第一,我們從Socket中讀到的是序列化了的LoggingEvent對象,所以,我們得首先反序列化這個對象,得到LoggingEvent對象;第二,得到這個對象後,我們要確定處理這個對象的方式,這裏我們就簡單處理,直接使用System.out打印出日誌消息即可。那麼,我們可以先把這個Server的框架代碼寫出來:

public static void main(String[] args) throws Exception {
    ServerSocket socket = new ServerSocket(4560);
    while (true) {
    Socket client = socket.accept();
    Thread t = new Thread(new LogRunner(client));
    t.start();
    }
}


    我們使用一個ServerSocket循環的監聽4560端口,得到請求後,就把這個客戶請求交給一個LogRunner線程去處理。
下面就是LogRunner的代碼:

static class LogRunner implements Runnable {
    private ObjectInputStream ois;

        public LogRunner(Socket client) {
            try {
                 this.ois = new ObjectInputStream(client.getInputStream());
            } catch (Exception e) {
        }
    }

@Override
    public void run() {
        try {
            while (true) {
                LoggingEvent event = (LoggingEvent) ois.readObject();
                System.out.println(event.getLoggerName() + ":  " + event.getMessage());
            }
        } catch (Exception e) {
        } finally {
        }
    }
}


    這裏面有幾個點需要特別注意:
    1,我們使用socket的InputStream創建了一個ObjectInputStream用來反序列化Logging Client發送的LoggingEvent對象。
    2,特別注意,我們在run方法中,使用了一個while(true)無線循環來不斷的讀入並將讀入的二進制內容轉成Object對象,並將Object對象再強轉成LoggingEvent。並且,當我們讀入一個LoggingEvent對象之後,並沒有去關閉ois。爲什麼要這樣做呢?其實需要考慮SocketAppender的工作方式。當一個Logging Client運行的時候,他對應的SocketAppender就會啓動,並創建一個到指定Logging Server的Socket連接。每當一個日誌消息到達SocketAppender之後,就會直接向這個Socket的OutputStream寫入序列化的對象數據,並通過flush發送到服務器端。換句話說,就是當一個Logging Client應用創建好之後,就會一直有一個Socket連接到服務器,這個連接是不會斷的,並且一旦有日誌消息到達,就直接發送序列化數據。所以我們的服務器端,就必須在得到一個Socket連接之後,一直維護這個連接,不斷的從連接中讀取數據,並且不能關閉這個連接。
     然後我們將這個應用先運行起來。再來看客戶端。
     首先來看看SocketAppender,他有幾個常用的配置:
    RemoteHost:指定遠端的Logging Server的地址。比如對於我們的應用來說,直接配成LocalHost就可以了。
    Port:指定遠端的Logging Server的端口。這裏,我們的服務器端監聽的是4560端口,所以,這裏就填4560。其實,默認情況下,SocketAppender就是請求的4560端口。
    Application:可以設置應用的名稱。因爲使用SocketAppender,就可以使用同一個專門的Logging Server來統一完成日誌記錄。那麼就有可能會有不同的應用提交日誌到Server上,怎麼區分日誌信息是哪個客戶端發過來的呢?就可以通過配置Application信息即可。具體這個信息從LoggingEvent中怎麼獲取,待會再介紹。
    reconnectionDelay:如果客戶端連接不上服務器端,那麼會在reconnectionDelay指定的時間之後重新連接。這裏需要注意的一個問題就是,客戶端發送日誌請求和服務器之間的通信問題。在網絡通信正常的情況下,每一條客戶端的日誌請求都會正常的發送給服務器端處理,這是沒有問題的。但是考慮網絡的不穩定性,就會出現以下的情況:
    1,當網絡發送包的頻率大於日誌頻率,會正常的按照日誌頻率發送信息;
    2,當網絡發送包的頻率低於日誌頻率,則只會按照網絡發送包的頻率來發送客戶端的日誌信息。這裏就有可能出現日誌消息丟失的問題;
    3,當服務器連接斷開,SocketAppender會嘗試重新連接服務器端,但是在這個斷開的過程當中,所有的日誌信息將會丟失。
    所以,在使用SocketAppender的時候,也需要考慮到網絡的因素。當然,我們也能自己實現一個Appender,當網絡斷開的時候,先將LoggingEvent對象暫時序列化到本地,當網絡再次連接上之後,再把緩存到本地的LoggingEvent對象發送。要完成這個功能,只要瞭解了Appender的工作原理就能比較輕鬆的完成。這個我們後面再看。
    下面,就完成客戶端的配置,並運行測試:
log4j.rootLogger=DEBUG,socket
log4j.appender.socket=org.apache.log4j.net.SocketAppender
log4j.appender.socket.RemoteHost=localhost
log4j.appender.socket.port=4560
log4j.appender.socket.application=localclient
    我們定義了一個socketAppender,並配置了服務器地址和端口號,最後爲我們的client取名爲localclient。
   下面來看看測試:

@Test
public void testSocketAppender() throws Exception{
    Logger logger = Logger.getLogger("cd.itcast");
    Logger barLogger = Logger.getLogger("cd.itcast.log");
    for (int i = 0; i < 100; i++) {
        logger.warn("logger warn");
        logger.debug("logger debug");
        barLogger.info("bar logger info");
        barLogger.debug("bar logger debug long long ");
    }
}


同樣的測試,運行100次,這次來運行,可以看到,速度非常的快,原因就是,使用SocketAppender,在客戶端根本不會對LoggingEvent做任何過多的處理,而直接就把序列化的對象發送到遠端server上,並且,序列化和發送的邏輯,還是獨立運行在一個單獨的線程中,對我們應用所在的線程性能沒有任何影響。
查看服務器端的控制檯輸出:
localclient:cd.itcast:  logger warn
localclient:cd.itcast:  logger debug
localclient:cd.itcast.log:  bar logger info
localclient:cd.itcast.log:  bar logger debug long long 
可以看到內容完整的輸出了。
其實,我們也沒有必要自己來寫這個Server端的應用,因爲Log4J爲我們實現了一個Server端:SimpleSocketServer。
這個SimpleSocketServer的使用非常簡單,他相當於把接受到的LoggingEvent作爲本地的日誌記錄事件,再使用在服務器端配置的Log4J環境來記錄日誌。畫個簡單的圖:

    在啓動SimpleSocketServer的時候,有兩個啓動選項,一個是監聽的端口,一個是配置文件地址,我們可以這樣測試,首先額外創建一個log4j.properties文件來控制SimpleSocketServer上面的Log4J環境。
log4j.rootLogger=DEBUG,file
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=%r [%t] %p %c %x - %m%n
log4j.appender.file.file=serverlog.log
    我們把這個配置文件命名爲log4jserver.properties,
    然後使用參數4560 log4jserver.properties來啓動SimpleSocketServer。
    再次運行我們的測試,可以發現,在應用目錄下多了log4jserver.log日誌文件,並且裏面的內容:
0 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Listening on port 4560
0 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Waiting to accept a new client.
29968 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Connected to client at /192.168.1.101
29968 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Starting new socket node.
29968 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Waiting to accept a new client.
87406 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Connected to client at /192.168.1.101
87406 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Starting new socket node.
87422 [main] INFO org.apache.log4j.net.SimpleSocketServer  - Waiting to accept a new client.
87422 [main] WARN cd.itcast  - logger warn
87437 [main] DEBUG cd.itcast  - logger debug
87437 [main] INFO cd.itcast.log  - bar logger info
87437 [main] DEBUG cd.itcast.log  - bar logger debug long long 
     確實爲我們客戶端的日誌消息。
    

    關於Appender,我們就先暫時看到這裏,至於後面的SMTPAppender和JdbcAppender,大家按照我們學習這些Appender的思路,去運用就可以了。


轉自:http://cd.itcast.cn/news/20130913/09502850126.shtml 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章