C++日誌庫log4cplus:SocketAppender記錄日誌到log Server
1.問題
C++程式在後臺執行時,可通過log4cplus記錄日誌。當C++程式執行在遠端伺服器上時,我們就需要遠端登陸到該伺服器才能檢視日誌。進一步,如果該C++程式一個並行程式或者分散式程式,為了檢視程式的執行狀態,我們就需要登陸到N臺伺服器上,tail -f xx.log.這種情形聽起來就很令人不爽,而實際上,很多服務端開發者都遇到過或正在遭受這個問題的困擾。
2.LoggingServer
作為Log4J的翻版,Log4cplus也提供了SockeAppender,可以通過SocketAppender將日誌輸出到一個指定的log server上,從而解決上述問題。
在Log4cplus的原始碼包中,有一個loggingServer目錄,該目錄中實現了一個LoggingServer。
在編譯Log4cplus時,會自動編譯該目錄,在目錄中生成loggingServer可執行檔案,當然可以自己make(需要依賴log4cplus庫)。
loggingServer使用方式如下:
./loggingserver 9000 log4cplus.properties
9000表示監聽的埠號(不需要地址,預設監聽本機地址)
log4cplus.properties是一個log4cplus的配置檔案,和普通的log4cplus配置檔案相同,loggingserver收到各個socket發來的日誌後,根據配置檔案資訊,將其寫入檔案。
下面是一個簡單的配置檔案示例:
log4cplus.rootLogger=DEBUG, STDOUT, ALL_MSGS log4cplus.appender.STDOUT=log4cplus::ConsoleAppender log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout #log4cplus.appender.STDOUT.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S} [%t] %-5p %c{2} %%%x%% - %m [%l]%n log4cplus.appender.STDOUT.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n #設定日誌追加到檔案尾 log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender #設定日誌檔案大小 log4cplus.appender.ALL_MSGS.MaxFileSize=100MB #設定生成日誌最大個數 log4cplus.appender.ALL_MSGS.MaxBackupIndex=10 #設定輸出日誌路徑 log4cplus.appender.ALL_MSGS.File=log/test.log log4cplus.appender.ALL_MSGS.layout=log4cplus::PatternLayout #設定日誌列印格式 #log4cplus.appender.ALL_MSGS.layout.ConversionPattern=|%D:%d{%Q}|%p|%t|%l|%m|%n log4cplus.appender.ALL_MSGS.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n #匹配相同日誌級別,只有debug日誌才輸入到該檔案中 #log4cplus.appender.ALL_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter #log4cplus.appender.DEBUG_MSGS.filters.1.LogLevelToMatch=DEBUG #log4cplus.appender.DEBUG_MSGS.filters.1.AcceptOnMatch=true #log4cplus.appender.DEBUG_MSGS.filters.2=log4cplus::spi::DenyAllFilter
loggingserver本身十分簡單,其程式碼如下(當然如果覺得它不爽,你也可以自己實現一個更cool的):
// Module: LOG4CPLUS // File: loggingserver.cxx // Created: 5/2003 // Author: Tad E. Smith // // // Copyright 2003-2010 Tad E. Smith // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <cstdlib> #include <iostream> #include <log4cplus/configurator.h> #include <log4cplus/socketappender.h> #include <log4cplus/helpers/socket.h> #include <log4cplus/thread/threads.h> #include <log4cplus/spi/loggingevent.h> namespace loggingserver { class ClientThread : public log4cplus::thread::AbstractThread { public: ClientThread(log4cplus::helpers::Socket clientsock) : clientsock(clientsock) { std::cout << "Received a client connection!!!!" << std::endl; } ~ClientThread() { std::cout << "Client connection closed." << std::endl; } virtual void run(); private: log4cplus::helpers::Socket clientsock; }; } int main(int argc, char** argv) { if(argc < 3) { std::cout << "Usage: port config_file" << std::endl; return 1; } int port = std::atoi(argv[1]); const log4cplus::tstring configFile = LOG4CPLUS_C_STR_TO_TSTRING(argv[2]); log4cplus::PropertyConfigurator config(configFile); config.configure(); log4cplus::helpers::ServerSocket serverSocket(port); if (!serverSocket.isOpen()) { std::cout << "Could not open server socket, maybe port " << port << " is already in use." << std::endl; return 2; } while(1) { loggingserver::ClientThread *thr = new loggingserver::ClientThread(serverSocket.accept()); thr->start(); } return 0; } //////////////////////////////////////////////////////////////////////////////// // loggingserver::ClientThread implementation //////////////////////////////////////////////////////////////////////////////// void loggingserver::ClientThread::run() { while(1) { if(!clientsock.isOpen()) { return; } log4cplus::helpers::SocketBuffer msgSizeBuffer(sizeof(unsigned int)); if(!clientsock.read(msgSizeBuffer)) { return; } unsigned int msgSize = msgSizeBuffer.readInt(); log4cplus::helpers::SocketBuffer buffer(msgSize); if(!clientsock.read(buffer)) { return; } log4cplus::spi::InternalLoggingEvent event = log4cplus::helpers::readFromBuffer(buffer); log4cplus::Logger logger = log4cplus::Logger::getInstance(event.getLoggerName()); logger.callAppenders(event); } }
3.應用程式中SocketAppender配置
前面啟動了LoggingServer,下面說一下需要收集其日誌的各個應用程式中配置。
說白了,就是在原來的基礎上加一個SocketAppender,SocketAppender會自動將日誌傳送給loggingserver。這樣直接在loggingserver上就可以檢視所有伺服器上的日誌資訊啦。哥終於不用再擔心你們的執行狀態啦!
log4cplus.rootLogger=DEBUG, STDOUT, ALL_MSGS,RemoteServer
log4cplus.appender.STDOUT=log4cplus::ConsoleAppender
log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout
#log4cplus.appender.STDOUT.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S} [%t] %-5p %c{2} %%%x%% - %m [%l]%n
log4cplus.appender.STDOUT.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n
#設定日誌追加到檔案尾
log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender
#設定日誌檔案大小
log4cplus.appender.ALL_MSGS.MaxFileSize=100MB
#設定生成日誌最大個數
log4cplus.appender.ALL_MSGS.MaxBackupIndex=10
#設定輸出日誌路徑
log4cplus.appender.ALL_MSGS.File=log/test.log
log4cplus.appender.ALL_MSGS.layout=log4cplus::PatternLayout
#設定日誌列印格式
#log4cplus.appender.ALL_MSGS.layout.ConversionPattern=|%D:%d{%Q}|%p|%t|%l|%m|%n
log4cplus.appender.ALL_MSGS.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n
#匹配相同日誌級別,只有debug日誌才輸入到該檔案中
#log4cplus.appender.ALL_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter
#log4cplus.appender.DEBUG_MSGS.filters.1.LogLevelToMatch=DEBUG
#log4cplus.appender.DEBUG_MSGS.filters.1.AcceptOnMatch=true
#log4cplus.appender.DEBUG_MSGS.filters.2=log4cplus::spi::DenyAllFilter
log4cplus.appender.RemoteServer=log4cplus::SocketAppender
log4cplus.appender.RemoteServer.host=localhost
log4cplus.appender.RemoteServer.port=9000
上面第一行的RemoteServer和最後三行即為新增一個SocketAppender。將日誌傳送到遠端的loggingserver。
當然對本地的日誌記錄沒有影響,本地仍正常記錄。
同時我們還可以設定多個RemoetServer....,不過貌似也沒這個必要...
4.更加高階大氣上檔次
當然我們可以實現自己的loggingserver,更加的高階大氣上檔次。
a.將來自不同客戶端的日誌分別儲存;
b.不同級別的日誌單獨儲存;
c.在發現錯誤時,郵件通知;
d.實現一個B/S的日誌瀏覽工具;
e.日誌入庫;
f.存入HDFS,用Hadoop Map/Reduce挖掘;
g.接入Storm(實時流處理框架)對其進行分析;
...