http://my.oschina.net/lovecxx/blog/185951html
目錄[-]java
Log4cplus使用指南
linux
log4cplus是C++編寫的開源的日誌系統,前身是java編寫的log4j系統,受Apache Software License保護,做者是Tad E. Smith。ios
log4cplus具備線程安全、靈活、以及多粒度控制的特色,經過將日誌劃分優先級使其能夠面向程序調試、運行、測試、和維護等全生命週期。你能夠選擇將日誌輸出到屏幕、文件、NT event log、甚至是遠程服務器;經過指定策略對日誌進行按期備份等等。緩存
1- 解壓: gzip -cd log4cplus-x.x.x.tar.gz | tar -xf -安全
2- 進入log4cplus根目錄: cd log4cplus-x.x.x服務器
3- 產生Makefile: ./configure --prefix=/where/to/install -enable-threads=no網絡
若是須要指定安裝路徑可以使用--prefix參數, 不然將缺省安裝到/usr/local目錄下。另外,若是須要單線程版本可經過參數-enable-threads=no指定, 不然默認將安裝多線程版本。多線程
對於HP-UNIX平臺用戶, 因爲aCC編譯器選項兼容性問題,請另外加入參數CXXFLAGS=」-AA -w」(單線程版本)或CXXFLAGS=」-AA –mt -w」(多線程版本)。app
4- 建立: make
對於HP-UNIX用戶,因爲aCC編譯器不包含-Wall選項來顯示全部警告,建立時將致使無效的-W參數錯誤,請修改/log4cplus-x.x.x/src目錄下的Makefile,將AM_CPPFLAGS = -Wall 行的-Wall選項刪除或註釋掉。
此外,某些HP-UNIX平臺的套接字鏈接接受函數accept()第三個參數要求爲int*,而在socket-unix.cxx源文件153行實現中實際傳入的是socklen_t*類型,平臺並不支持,也將致使編譯錯誤。解決方法是將源代碼該行中的傳入參數強制轉換爲int*類型便可。
注意AIX和Linux平臺目前並無上述兩處建立錯誤。
對於AIX平臺用戶請保證建立時使用的編譯器是xlC而不是g++,不然將致使log4cplus腳本配置功能運行時產生段異常,生成core文件。有鑑於此,也請保證HP-UNIX用戶儘可能使用aCC編譯器進行建立。
5- 建立/log4cplus/tests目錄下的測試用例: make check
6- 安裝: make install
安裝成功後將在/usr/local目錄或指定的目錄下建立include和lib兩個子目錄及相應文件。其中include目錄包含頭文件,lib目錄包含最終打包生成的靜態和動態庫。在動態鏈接log4cplus庫時請使用-llog4cplus選項。
類名 |
說明 |
Filter |
過濾器,過濾輸出消息。過濾器,解決哪些信息須要輸出的問題,好比DEBUG,WARR,INFO等的輸出控制 |
Layout |
佈局器,控制輸出消息的格式。格式化輸出信息,解決了如何輸出的問題。 |
Appender |
掛接器,與佈局器和過濾器緊密配合,將特定格式的消息過濾後輸出到所掛接的設備終端如屏幕,文件等等)。接收日誌的各個設備,如控制檯、文件、網絡等。解決了輸出到哪裏去的問題 |
Logger |
記錄器,保存並跟蹤對象日誌信息變動的實體,當你須要對一個對象進行記錄時,就須要生成一個logger。日誌模塊,程序中惟一一個必須得使用的模塊,解決了在哪裏使用日誌的問題。 |
Hierarchy |
分類器,層次化的樹型結構,用於對被記錄信息的分類,層次中每個節點維護一個logger的全部信息。 |
LogLevel |
優先權,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。 |
使用log4cplus有六個基本步驟:
l 實例化一個封裝了輸出介質的appender對象;
l 實例化一個封裝了輸出格式的layout對象;
l 將layout對象綁定(attach)到appender對象;如省略此步驟,簡單佈局器SimpleLayout(參見5.1小節)對象會綁定到logger。
l 實例化一個封裝了日誌輸出logger對象,並調用其靜態函數getInstance()得到實例,log4cplus::Logger::getInstance("logger_name");
l 將appender對象綁定(attach)到logger對象;
l 設置logger的優先級,如省略此步驟,各類有限級的日誌都將被輸出。
下面經過一些例子來了解log4cplus的基本使用。
/* *標準使用,嚴格實現步驟1-6。 */ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h> #include <log4cplus/layout.h>
using namespace log4cplus; using namespace log4cplus::helpers;
int main() { /* step 1: Instantiate an appender object */ SharedObjectPtr<Appender> _append (new ConsoleAppender()); _append->setName("append for test");
/* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr<Layout> _layout(new PatternLayout(pattern));
/* step 3: Attach the layout object to the appender */ _append->setLayout( _layout );
/* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test");
/* step 5: Attach the appender object to the logger */ _logger.addAppender(_append);
/* step 6: Set a priority for the logger */ _logger.setLogLevel(ALL_LOG_LEVEL);
/* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") return 0; } |
輸出結果:
10/14/04 09:06:24 - This is the FIRST log message... [main.cpp:31] 10/14/04 09:06:25 - This is the SECOND log message... [main.cpp:33] |
/* *簡潔使用,僅實現步驟1、4、5。 */ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h>
using namespace log4cplus; using namespace log4cplus::helpers;
int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test");
/* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test");
/* step 5: Attach the appender object to the logger */ _logger.addAppender(_append);
/* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...")
return 0; } |
輸出結果:
DEBUG - This is the FIRST log message... WARN - This is the SECOND log message... |
/* *iostream模式,appender輸出到控制檯。 */ #include<log4cplus/logger.h> #include<log4cplus/consoleappender.h> #include<iomanip> usingnamespacelog4cplus;
int main() { /*step1:Instantiateanappenderobject*/ SharedAppenderPtr_append(new ConsoleAppender()); _append->setName("appendtest");
/*step4:Instantiatealoggerobject*/ Logger_logger = Logger::getInstance("test");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ LOG4CPLUS_TRACE(_logger, "Thisis" << "justat" << "est." << std::endl) LOG4CPLUS_DEBUG(_logger, "Thisisabool:" << true) LOG4CPLUS_INFO(_logger, "Thisisachar:" << 'x') LOG4CPLUS_WARN(_logger, "Thisisaint:" << 1000) LOG4CPLUS_ERROR(_logger, "Thisisalong(hex):" << std::hex << 100000000) LOG4CPLUS_FATAL(_logger, "Thisisadouble:" << std::setprecision(15) << 1.2345234234)
return0; } |
輸出結果:
DEBUG-Thisisabool:1 INFO-Thisisachar:x WARN-Thisisaint:1000 ERROR-Thisisalong(hex):5f5e100 FATAL-Thisisadouble:1.2345234234 |
/* *文件模式,appender輸出到文件。 */ #include<log4cplus/logger.h> #include<log4cplus/fileappender.h> usingnamespacelog4cplus;
int main() { /*step1:Instantiateanappenderobject*/ SharedAppenderPtr_append(new FileAppender("Test.log")); _append->setName("filelogtest");
/*step4:Instantiatealoggerobject*/ Logger_logger = Logger::getInstance("test.subtestof_filelog");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ for (int i = 0; i < 5; ++i) { LOG4CPLUS_DEBUG(_logger, "Enteringloop#" << i << "Endline#") }
return0; } |
輸出結果(Test.log文件):
DEBUG-Enteringloop#0Endline# DEBUG-Enteringloop#1Endline# DEBUG-Enteringloop#2Endline# DEBUG-Enteringloop#3Endline# DEBUG-Enteringloop#4Endline# |
LogLog類實現了debug, warn, error 函數用於logcplus運行時顯示log4cplus自身的調試、警告或錯誤信息,是對標準輸出的簡單封裝,它也能夠用來進行簡單的日誌輸出。LogLog 同時提供了兩個方法來進一步控制所輸出的信息,其中setInternalDebugging()方法用來控制是否屏蔽輸出信息中的調試信息,當輸入參數爲false則屏蔽,缺省設置爲false。 setQuietMode()方法用來控制是否屏蔽全部輸出信息,當輸入參數爲true則屏蔽,缺省設置爲false。
/* 經過loglog來控制輸出調試、警告或錯誤信息,appender輸出到屏幕。 */ #include <iostream> #include <log4cplus/helpers/loglog.h> using namespace log4cplus::helpers;
void printMsgs(void) { std::cout << "Entering printMsgs()..." << std::endl; LogLog::getLogLog()->debug("This is a Debug statement..."); LogLog::getLogLog()->warn("This is a Warning..."); LogLog::getLogLog()->error("This is a Error..."); std::cout << "Exiting printMsgs()..." << std::endl << std::endl; }
int main() { printMsgs(); std::cout << "Turning on debug..." << std::endl; LogLog::getLogLog()->setInternalDebugging(true); printMsgs(); std::cout << "Turning on quiet mode..." << std::endl; LogLog::getLogLog()->setQuietMode(true); printMsgs(); return 0; } |
輸出結果:
EnteringprintMsgs()... log4cplus:WARNThisisaWarning... log4cplus:ERRORThisisaError... ExitingprintMsgs()... Turningondebug... EnteringprintMsgs()... log4cplus:ThisisaDebugstatement... log4cplus:WARNThisisaWarning... log4cplus:ERRORThisisaError... ExitingprintMsgs()... Turningonquietmode... EnteringprintMsgs()... ExitingprintMsgs()... |
注意輸出信息中老是包含"log4cplus:"前綴,若是須要定製使其使用其餘的前綴請參見9.2小節。
log4cplus在頭文件loggingmacros.h中提供瞭如下的日誌輸出宏:
LOG4CPLUS_TRACE_METHOD(logger,logEvent)
LOG4CPLUS_TRACE(logger,logEvent) LOG4CPLUS_TRACE_STR(logger,logEvent)
LOG4CPLUS_DEBUG(logger,logEvent) LOG4CPLUS_DEBUG_STR(logger,logEvent)
LOG4CPLUS_INFO(logger,logEvent) LOG4CPLUS_INFO_STR(logger,logEvent)
LOG4CPLUS_WARN(logger,logEvent) LOG4CPLUS_WARN_STR(logger,logEvent)
LOG4CPLUS_ERROR(logger,logEvent) LOG4CPLUS_ERROR_STR(logger,logEvent)
LOG4CPLUS_FATAL(logger,logEvent) LOG4CPLUS_FATAL_STR(logger,logEvent) |
其中logger 爲Logger實例名稱,logEvent爲日誌內容。因爲log4cplus選用C++的流機制進行日誌輸出,所以爲了區分包含<<運算符和不包含<<運算符的日誌內容,分別提供了LOG4CPLUS_XXXX和LOG4CPLUS_XXXX_STR兩種日誌輸出宏。 另外,日誌輸出宏LOG4CPLUS_TRACE_METHOD主要用來跟蹤方法的調用軌跡。
log4cplus經過佈局器(Layouts)來控制輸出的格式,log4cplus提供了三種類型的Layouts,分別是SimpleLayout、PatternLayout、和TTCCLayout。
一種簡單格式的佈局器,在輸出的原始信息以前加上LogLevel和一個"-",若是初始化時沒有將佈局器附加到掛接器,則默認使用SimpleLayout。
如下代碼片斷演示瞭如何使用SimpleLayout。
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::auto_ptr<Layout> _layout(new log4cplus::SimpleLayout()); /* step 3: Attach the layout object to the appender */ _append->setLayout(_layout); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the simple formatted log message...") ... ... |
輸出結果:
DEBUG - This is the simple formatted log message... |
一種有詞法分析功能的模式佈局器,相似於C語言的printf()函數,可以對預約義的轉換標識符(conversion specifiers)進行解析,轉換成特定格式輸出。
如下代碼片斷演示瞭如何使用PatternLayout。
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr _append (new ConsoleAppender()); _append->setName("append for test");
/* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr<Layout> _layout(new PatternLayout(pattern));
/* step 3: Attach the layout object to the appender */ _append->setLayout(_layout);
/* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test_logger.subtest");
/* step 5: Attach the appender object to the logger */ _logger.addAppender(_append);
/* log activity */ LOG4CPLUS_DEBUG(_logger, "teststr") ... ... |
輸出結果:
10/16/04 18:51:25 - teststr [main.cpp:51] |
PatterLayout支持的轉換標識符主要包括:
(1)"%%",轉義爲%, 即,std::string pattern = "%%" 時輸出"%"。
(2)"%c",輸出logger名稱,好比std::string pattern ="%c" 時輸出: "test_logger.subtest", 也能夠控制logger名稱的顯示層次,好比"%c{1}"時輸出"test_logger",其中數字表示層次。
(3)"%D",顯示本地時間,當std::string pattern ="%D" 時輸出:"2004-10-16 18:55:45",%d顯示標準時間,因此當std::string pattern ="%d" 時輸出"2004-10-16 10:55:45" (由於北京時間位於東8區,差8個小時)。
能夠經過%d{...}定義更詳細的顯示格式,好比%d{%H:%M:%s}表示要顯示小時:分鐘:秒。大括號中可顯示的預約義標識符以下:
%a -- 表示禮拜幾,英文縮寫形式,好比"Fri"
%A -- 表示禮拜幾,好比"Friday"
%b -- 表示幾月份,英文縮寫形式,好比"Oct"
%B -- 表示幾月份,"October"
%c -- 標準的日期+時間格式,如 "Sat Oct 16 18:56:19 2004"
%d -- 表示今天是這個月的幾號(1-31)"16"
%H -- 表示當前時刻是幾時(0-23),如 "18"
%I -- 表示當前時刻是幾時(1-12),如 "6"
%j -- 表示今天是哪一天(1-366),如 "290"
%m -- 表示本月是哪一月(1-12),如 "10"
%M -- 表示當前時刻是哪一分鐘(0-59),如 "59"
%p -- 表示如今是上午仍是下午, AM or PM
%q -- 表示當前時刻中毫秒部分(0-999),如 "237"
%Q -- 表示當前時刻中帶小數的毫秒部分(0-999.999),如 "430.732"
%S -- 表示當前時刻的多少秒(0-59),如 "32"
%U -- 表示本週是今年的第幾個禮拜,以週日爲第一天開始計算(0-53),如 "41"
%w -- 表示禮拜幾,(0-6, 禮拜天爲0),如 "6"
%W -- 表示本週是今年的第幾個禮拜,以週一爲第一天開始計算(0-53),如 "41"
%x -- 標準的日期格式,如 "10/16/04"
%X -- 標準的時間格式,如 "19:02:34"
%y -- 兩位數的年份(0-99),如 "04"
%Y -- 四位數的年份,如 "2004"
%Z -- 時區名,好比 "GMT"
(4)"%F",輸出當前記錄器所在的文件名稱,好比std::string pattern ="%F" 時輸出: "main.cpp"。
(5)"%L",輸出當前記錄器所在的文件行號,好比std::string pattern ="%L" 時輸出: "51"
(6)"%l",輸出當前記錄器所在的文件名稱和行號,好比std::string pattern ="%l" 時輸出"main.cpp:51"。
(7)"%m",輸出原始信息,好比std::string pattern ="%m" 時輸出: "teststr",即上述代碼中LOG4CPLUS_DEBUG的第二個參數,這種實現機制能夠確保原始信息被嵌入到帶格式的信息中。
(8)"%n",換行符,沒什麼好解釋的。
(9)"%p",輸出LogLevel,好比std::string pattern ="%p" 時輸出: "DEBUG"。
(10)"%t",輸出記錄器所在的線程ID,好比std::string pattern ="%t" 時輸出: "1075298944"。
(11)"%x",嵌套診斷上下文NDC (nested diagnostic context) 輸出,從堆棧中彈出上下文信息,NDC能夠用對不一樣源的log信息(同時地)交叉輸出進行區分,關於NDC方面的詳細介紹會在下文中提到。
(12)格式對齊,好比std::string pattern ="%-10m"時表示左對齊,寬度是10,此時會輸出"teststr ",固然其它的控制字符也能夠相同的方式來使用,好比"%-12d","%-5p"等等。
是在PatternLayout基礎上發展的一種缺省的帶格式輸出的佈局器,其格式由時間,線程ID,Logger和NDC 組成(consists of time, thread, Logger and nested diagnostic context information, hence the name),於是得名, 關於NDC請參見6.4小節。
如下代碼片斷演示瞭如何使用TTCCLayout。
...... /*step1:Instantiateanappenderobject*/ SharedObjectPtr_append(new ConsoleAppender()); _append->setName("appendfortest");
/*step2:Instantiatealayoutobject*/ std::auto_ptr_layout(new TTCCLayout());
/*step3:Attachthelayoutobjecttotheappender*/ _append->setLayout(_layout);
/*step4:Instantiatealoggerobject*/ Logger_logger=Logger::getInstance("test_logger");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ LOG4CPLUS_DEBUG(_logger,"teststr") ...... |
輸出結果:
10-16-04 19:08:27,501 [1075298944] DEBUG test_logger <> - teststr |
TTCCLayout在構造時,有機會選擇顯示本地時間或GMT時間,缺省是按照本地時間顯示:TTCCLayout::TTCCLayout(bool use_gmtime = false)。
若是須要構造TTCCLayout對象時選擇GMT時間格式,則使用方式以下代碼片段所示。
... ... /* step 2: Instantiate a layout object */ std::auto_ptr _layout(new TTCCLayout(true)); ... ... |
輸出結果:
10-16-04 11:12:47,678 [1075298944] DEBUG test_logger <> - teststr |
log4cplus默認將輸出到控制檯,提供ConsoleAppender用於操做。示例代碼請參見4.2.1、4.2.2或4.2.3小節,這裏再也不贅述。
log4cplus提供了三個類用於文件操做,它們是FileAppender類、RollingFileAppender類、DailyRollingFileAppender類。
實現了基本的文件操做功能,構造函數以下:
FileAppender ::FileAppender(const log4cplus::tstring& filename,
LOG4CPLUS_OPEN_MODE_TYPE mode =
LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc,
bool immediateFlush = true);
filename : 文件名
mode : 文件類型,可選擇的文件類型包括app、ate、binary、in、out、trunc,由於實際上只是對stl的一個簡單包裝,這裏就很少講了。缺省是trunc,表示將先前文件刪除。
immediateFlush : 緩衝刷新標誌,若是爲true表示每向文件寫一條記錄就刷新一次緩存,不然直到FileAppender被關閉或文件緩存已滿才更新文件,通常是要設置true的,好比你往文件寫的過程當中出現了錯誤(如程序非正常退出),即便文件沒有正常關閉也能夠保證程序終止時刻以前的全部記錄都會被正常保存。
FileAppender類的使用狀況請參考4.2.5小節,這裏再也不贅述。
實現能夠滾動轉儲的文件操做功能,構造函數以下:
RollingFileAppender::RollingFileAppender(const log4cplus::tstring& filename,
long maxFileSize,
int maxBackupIndex,
bool immediateFlush)
filename : 文件名
maxFileSize : 文件的最大尺寸
maxBackupIndex : 最大記錄文件數
immediateFlush : 緩衝刷新標誌
RollingFileAppender類能夠根據你預先設定的大小來決定是否轉儲,當超過該大小,後續log信息會另存到新文件中,除了定義每一個記錄文件的大小以外,你還要肯定在RollingFileAppender類對象構造時最多須要多少個這樣的記錄文件(maxBackupIndex+1),當存儲的文件數目超過maxBackupIndex+1時,會刪除最先生成的文件,保證整個文件數目等於maxBackupIndex+1。而後繼續記錄,好比如下代碼片斷:
... ... #define LOOP_COUNT 200000 SharedAppenderPtr _append(new RollingFileAppender("Test.log", 5*1024, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr(new TTCCLayout()) ); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot(); Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); for(int i=0; i < LOOP_COUNT; ++i) { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ... |
輸出結果:
運行後會產生6個輸出文件,Test.log、Test.log.1、Test.log.2、Test.log.3、Test.log.4、Test.log.5其中Test.log存放着最新寫入的信息,而最後一個文件中並不包含第一個寫入信息,說明已經被不斷更新了。
須要指出的是,這裏除了Test.log以外,每一個文件的大小都是200K,而不是咱們想像中的5K,這是由於log4cplus中隱含定義了文件的最小尺寸是200K,只有大於200K的設置才生效,<= 200k的設置都會被認爲是200K。
實現根據頻度來決定是否轉儲的文件轉儲功能,構造函數以下:
DailyRollingFileAppender::DailyRollingFileAppender(const log4cplus::tstring& filename,
DailyRollingFileSchedule schedule,
bool immediateFlush,
int maxBackupIndex)
filename : 文件名
schedule : 存儲頻度
immediateFlush : 緩衝刷新標誌
maxBackupIndex : 最大記錄文件數
DailyRollingFileAppender類能夠根據你預先設定的頻度來決定是否轉儲,當超過該頻度,後續log信息會另存到新文件中,這裏的頻度包括:MONTHLY(每個月)、WEEKLY(每週)、DAILY(每日)、TWICE_DAILY(每兩天)、HOURLY(每時)、MINUTELY(每分)。maxBackupIndex的含義同上所述,好比如下代碼片斷:
... ... SharedAppenderPtr _append(new DailyRollingFileAppender("Test.log", MINUTELY, true, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr(new TTCCLayout()) ); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot(); Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); for(int i=0; i < LOOP_COUNT; ++i) { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ... |
輸出結果:
運行後會以分鐘爲單位,分別生成名爲Test.log.2004-10-17-03-03、Test.log.2004-10-17-03-04和Test.log.2004-10-17-03-05這樣的文件。
須要指出的是這裏的"頻度"並非你寫入文件的速度,實際上是否轉儲的標準並不依賴你寫入文件的速度,而是依賴於寫入的那一時刻是否知足了頻度條件,便是否超過了以分鐘、小時、周、月爲單位的時間刻度,若是超過了就另存。
log4cplus提供了SocketAppender,實現了C/S方式的日誌記錄,用於支持重定向到遠程服務器。
(1) 定義一個SocketAppender類型的掛接器
SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));
(2) 把該掛接器加入到logger中
Logger::getRoot().addAppender(_append);
(3) SocketAppender類型不須要Layout, 直接調用宏就能夠將信息發往loggerServer了LOG4CPLUS_INFO(Logger::getRoot(), "This is a test: ")
注意這裏對宏的調用實際上是調用了SocketAppender::append(),裏面有一個數據傳輸約定,即先發送一個後續數據的總長度,而後再發送實際的數據:
... ... SocketBuffer buffer = convertToBuffer(event, serverName); SocketBuffer msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE); msgBuffer.appendSize_t(buffer.getSize()); msgBuffer.appendBuffer(buffer); ... ... |
(1) 定義一個ServerSocket
ServerSocket serverSocket(port);
(2) 調用accept函數建立一個新的socket與客戶端鏈接
Socket sock = serverSocket.accept();
(3) 此後便可用該sock進行數據read/write了,形如(完整代碼見6.3.3小節):
SocketBuffer msgSizeBuffer(sizeof(unsigned int));
if(!clientsock.read(msgSizeBuffer)){
return;
}
unsigned int msgSize = msgSizeBuffer.readInt();
SocketBuffer buffer(msgSize);
if(!clientsock.read(buffer)){
return;
}
(4) 爲了將讀到的數據正常顯示出來,須要將SocketBuffer存放的內容轉換成InternalLoggingEvent格式:
log4cplus::spi::InternalLoggingEvent event = readFromBuffer(buffer);
而後輸出:
Logger logger = Logger::getInstance(event.getLoggerName());
logger.callAppenders(event);
注意read/write是按照阻塞方式實現的,意味着對其調用直到知足了所接收或發送的個數才返回。
如下是服務器端代碼。
#include <log4cplus/config.h> #include <log4cplus/configurator.h> #include <log4cplus/consoleappender.h> #include <log4cplus/socketappender.h> #include <log4cplus/helpers/loglog.h> #include <log4cplus/helpers/socket.h> #include <log4cplus/helpers/threads.h> #include <log4cplus/spi/loggerimpl.h> #include <log4cplus/spi/loggingevent.h> #include <iostream> using namespace std; using namespace log4cplus; using namespace log4cplus::helpers; using namespace log4cplus::thread;
namespace loggingserver { class ClientThread : public AbstractThread { public: ClientThread(Socket clientsock) : clientsock(clientsock) { cout << "Received a client connection!!!!" << endl; }
~ClientThread() { cout << "Client connection closed." << endl; }
virtual void run();
private: Socket clientsock; };
}
int main(int argc, char** argv) { if (argc < 3) { cout << "Usage: port config_file" << endl; return 1; } int port = atoi(argv[1]); tstring configFile = LOG4CPLUS_C_STR_TO_TSTRING(argv[2]);
PropertyConfigurator config(configFile); config.configure();
ServerSocket serverSocket(port); 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; } SocketBuffer msgSizeBuffer(sizeof(unsigned int)); if (!clientsock.read(msgSizeBuffer)) { return; } unsigned int msgSize = msgSizeBuffer.readInt(); SocketBuffer buffer(msgSize); if (!clientsock.read(buffer)) { return; } spi::InternalLoggingEvent event = readFromBuffer(buffer); Logger logger = Logger::getInstance(event.getLoggerName()); logger.callAppenders(event); } } |
如下是客戶端代碼。
#include <log4cplus/logger.h> #include <log4cplus/socketappender.h> #include <log4cplus/loglevel.h> #include <log4cplus/tstring.h> #include <log4cplus/helpers/threads.h> #include <iomanip>
using namespace std; using namespace log4cplus;
int main(int argc, char **argv) { log4cplus::helpers::sleep(1); tstring serverName = (argc > 1 ? LOG4CPLUS_C_STR_TO_TSTRING(argv[1]) : tstring()); //tstring host = LOG4CPLUS_TEXT("192.168.2.10"); tstring host = LOG4CPLUS_TEXT("127.0.0.1"); SharedAppenderPtr append_1(new SocketAppender(host, 9998, serverName)); append_1->setName( LOG4CPLUS_TEXT("First") ); Logger::getRoot().addAppender(append_1);
Logger root = Logger::getRoot(); Logger test = Logger::getInstance( LOG4CPLUS_TEXT("socket.test") );
LOG4CPLUS_DEBUG(root, "This is" << " a reall" << "y long message." << endl << "Just testing it out" << endl << "What do you think?") test.setLogLevel(NOT_SET_LOG_LEVEL); LOG4CPLUS_DEBUG(test, "This is a bool: " << true) LOG4CPLUS_INFO(test, "This is a char: " << 'x') LOG4CPLUS_INFO(test, "This is a short: " << (short)-100) LOG4CPLUS_INFO(test, "This is a unsigned short: " << (unsigned short)100) log4cplus::helpers::sleep(0, 500000); LOG4CPLUS_INFO(test, "This is a int: " << (int)1000) LOG4CPLUS_INFO(test, "This is a unsigned int: " << (unsigned int)1000) LOG4CPLUS_INFO(test, "This is a long(hex): " << hex << (long)100000000) LOG4CPLUS_INFO(test, "This is a unsigned long: " << (unsigned long)100000000) LOG4CPLUS_WARN(test, "This is a float: " << (float)1.2345) LOG4CPLUS_ERROR(test, "This is a double: " << setprecision(15) << (double)1.2345234234) LOG4CPLUS_FATAL(test, "This is a long double: " << setprecision(15) << (long double)123452342342.342)
return 0; } |
log4cplus中的嵌入診斷上下文(Nested Diagnostic Context),即NDC。對log系統而言,當輸入源可能不止一個,而只有一個輸出時,每每須要分辯所要輸出消息的來源,好比服務器處理來自不一樣客戶端的消息時就須要做此判斷,NDC能夠爲交錯顯示的信息打上一個標記(stamp),使得辨認工做看起來比較容易些。這個標記是線程特有的,利用了線程局部存儲機制,稱爲線程私有數據(Thread-Specific Data,或TSD)。相關定義以下,包括定義、初始化、獲取、設置和清除操做:
linux pthread #define LOG4CPLUS_THREAD_LOCAL_TYPE pthread_key_t* #define LOG4CPLUS_THREAD_LOCAL_INIT ::log4cplus::thread::createPthreadKey() #define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) pthread_getspecific(*key) #defineLOG4CPLUS_SET_THREAD_LOCAL_VALUE(key,value) \ pthread_setspecific(*key, value) #define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) pthread_key_delete(*key)
win32 #define LOG4CPLUS_THREAD_LOCAL_TYPE DWORD #define LOG4CPLUS_THREAD_LOCAL_INIT TlsAlloc() #define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) TlsGetValue(key) #define LOG4CPLUS_SET_THREAD_LOCAL_VALUE( key, value ) \ TlsSetValue(key, static_cast(value)) #define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) TlsFree(key) |
使用起來比較簡單,在某個線程中:
NDC& ndc = log4cplus::getNDC(); ndc.push("ur ndc string"); LOG4CPLUS_DEBUG(logger, "this is a NDC test"); ... ... ndc.pop(); ... ... LOG4CPLUS_DEBUG(logger, "There should be no NDC..."); ndc.remove(); |
輸出結果(當設定輸出格式爲TTCCLayout時):
10-21-04 21:32:58, [3392] DEBUG test - this is a NDC test 10-21-04 21:32:58, [3392] DEBUG test <> - There should be no NDC... |
也能夠在自定義的輸出格式中使用NDC(用%x) ,好比:
... ... std::string pattern = "NDC:[%x] - %m %n"; std::auto_ptr _layout(new PatternLayout(pattern)); ... ... LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") NDC& ndc = log4cplus::getNDC(); ndc.push("ur ndc string"); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") ndc.pop(); ndc.remove(); ... ... |
輸出結果:
NDC:[] - This is the FIRST log message... NDC:[ur ndc string] - This is the SECOND log message... |
另一種更簡單的使用方法是在線程中直接用NDCContextCreator:
NDCContextCreator _first_ndc("ur ndc string");
G4CPLUS_DEBUG(logger, "this is a NDC test")
沒必要顯式地調用push/pop了,並且當出現異常時,可以確保push與pop的調用是匹配的。
log4cplus將輸出的log信息按照LogLevel(從低到高)分爲:
級別 |
說明 |
NOT_SET_LOG_LEVEL ( -1) |
接受缺省的LogLevel,若是有父logger則繼承它的LogLevel |
ALL_LOG_LEVEL (0) |
開放全部log信息輸出 |
TRACE_LOG_LEVEL (0) |
開放trace信息輸出(即ALL_LOG_LEVEL) |
DEBUG_LOG_LEVEL(10000) |
開放debug信息輸出 |
INFO_LOG_LEVEL (20000) |
開放info信息輸出 |
WARN_LOG_LEVEL (30000) |
開放warning信息輸出 |
ERROR_LOG_LEVEL(40000) |
開放error信息輸出 |
FATAL_LOG_LEVEL (50000) |
開放fatal信息輸出 |
OFF_LOG_LEVEL (60000) |
關閉全部log信息輸出 |
在log4cplus中,全部logger都經過一個層次化的結構(其實內部是hash表)來組織的,有一個Root級別的logger,能夠經過如下方法獲取:Logger root = Logger::getRoot();
用戶定義的logger都有一個名字與之對應,好比:Logger test = Logger::getInstance("test");
能夠定義該logger的子logger: Logger subTest = Logger::getInstance("test.subtest");
注意Root級別的logger只有經過getRoot方法獲取,Logger::getInstance("root")得到的是它的子對象而已。有了這些具備父子關係的logger以後可分別設置其LogLevel,好比:
root.setLogLevel( ... );
Test.setLogLevel( ... );
subTest.setLogLevel( ... );
各個logger能夠經過setLogLevel設置本身的優先級,當某個logger的LogLevel設置成NOT_SET_LOG_LEVEL時,該logger會繼承父logger的優先級,另外,若是定義了重名的多個logger, 對其中任何一個的修改都會同時改變其它logger。
log4cplus支持編譯時候和運行時刻利用日誌級別進行輸出過濾。編譯時刻經過以下的預約義變量進行過濾:
#define LOG4CPLUS_DISABLE_FATAL #define LOG4CPLUS_DISABLE_WARN #define LOG4CPLUS_DISABLE_ERROR #define LOG4CPLUS_DISABLE_INFO #define LOG4CPLUS_DISABLE_DEBUG #define LOG4CPLUS_DISABLE_TRACE |
運行時刻的過濾則經過使用Logger的setLogLevel設置日誌級別進行過濾。
#include "log4cplus/logger.h" #include "log4cplus/consoleappender.h" #include "log4cplus/loglevel.h" #include <iostream> using namespace std; using namespace log4cplus;
int main() { SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("test"); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot();
Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); LogLevelManager& llm = getLogLevelManager();
cout << endl << "Before Setting, Default LogLevel" << endl; LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root,"test.subtest:" << llm.toString(subTest.getChainedLogLevel()));
cout << endl << "Setting test.subtest to WARN" << endl; subTest.setLogLevel(WARN_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
cout << endl << "Setting test to TRACE" << endl; test.setLogLevel(TRACE_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
cout << endl << "Setting test.subtest to NO_LEVEL" << endl; subTest.setLogLevel(NOT_SET_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()) << '\n');
cout << "create a logger test_bak, named \"test_\", too. " << endl; Logger test_bak = Logger::getInstance("test"); cout << "Setting test to INFO, so test_bak also be set to INFO" << endl; test.setLogLevel(INFO_LOG_LEVEL); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test_bak: " << llm.toString(test_bak.getChainedLogLevel()));
return 0; } |
輸出結果:
Before Setting, Default LogLevel FATAL - root: DEBUG FATAL - test: DEBUG FATAL - test.subtest: DEBUG
Setting test.subtest to WARN FATAL - root: DEBUG FATAL - test: DEBUG FATAL - test.subtest: WARN
Setting test to TRACE FATAL - root: DEBUG FATAL - test: TRACE FATAL - test.subtest: WARN
Setting test.subtest to NO_LEVEL FATAL - root: DEBUG FATAL - test: TRACE FATAL - test.subtest: TRACE
create a logger test_bak, named "test_", too. Setting test to INFO, so test_bak also be set to INFO FATAL - test: INFO FATAL - test_bak: INFO |
#include "log4cplus/logger.h" #include "log4cplus/consoleappender.h" #include "log4cplus/loglevel.h" #include <iostream> using namespace std; using namespace log4cplus;
void ShowMsg(void) { LOG4CPLUS_TRACE(Logger::getRoot(),"info"); LOG4CPLUS_DEBUG(Logger::getRoot(),"info"); LOG4CPLUS_INFO(Logger::getRoot(),"info"); LOG4CPLUS_WARN(Logger::getRoot(),"info"); LOG4CPLUS_ERROR(Logger::getRoot(),"info"); LOG4CPLUS_FATAL(Logger::getRoot(),"info"); }
int main() { SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("test"); _append->setLayout(std::auto_ptr(new TTCCLayout())); Logger root = Logger::getRoot(); root.addAppender(_append);
cout << endl << "all-log allowed" << endl; root.setLogLevel(ALL_LOG_LEVEL); ShowMsg();
cout << endl << "trace-log and above allowed" << endl; root.setLogLevel(TRACE_LOG_LEVEL); ShowMsg();
cout << endl << "debug-log and above allowed" << endl; root.setLogLevel(DEBUG_LOG_LEVEL); ShowMsg();
cout << endl << "info-log and above allowed" << endl; root.setLogLevel(INFO_LOG_LEVEL); ShowMsg();
cout << endl << "warn-log and above allowed" << endl; root.setLogLevel(WARN_LOG_LEVEL); ShowMsg();
cout << endl << "error-log and above allowed" << endl; root.setLogLevel(ERROR_LOG_LEVEL); ShowMsg();
cout << endl << "fatal-log and above allowed" << endl; root.setLogLevel(FATAL_LOG_LEVEL); ShowMsg();
cout << endl << "log disabled" << endl; root.setLogLevel(OFF_LOG_LEVEL); ShowMsg();
return 0; } |
輸出結果:
all-log allowed 10-17-04 10:11:40,587 [1075298944] TRACE root <> - info 10-17-04 10:11:40,590 [1075298944] DEBUG root <> - info 10-17-04 10:11:40,591 [1075298944] INFO root <> - info 10-17-04 10:11:40,591 [1075298944] WARN root <> - info 10-17-04 10:11:40,592 [1075298944] ERROR root <> - info 10-17-04 10:11:40,592 [1075298944] FATAL root <> - info
trace-log and above allowed 10-17-04 10:11:40,593 [1075298944] TRACE root <> - info 10-17-04 10:11:40,593 [1075298944] DEBUG root <> - info 10-17-04 10:11:40,594 [1075298944] INFO root <> - info 10-17-04 10:11:40,594 [1075298944] WARN root <> - info 10-17-04 10:11:40,594 [1075298944] ERROR root <> - info 10-17-04 10:11:40,594 [1075298944] FATAL root <> - info
debug-log and above allowed 10-17-04 10:11:40,595 [1075298944] DEBUG root <> - info 10-17-04 10:11:40,595 [1075298944] INFO root <> - info 10-17-04 10:11:40,596 [1075298944] WARN root <> - info 10-17-04 10:11:40,596 [1075298944] ERROR root <> - info 10-17-04 10:11:40,596 [1075298944] FATAL root <> - info
info-log and above allowed 10-17-04 10:11:40,597 [1075298944] INFO root <> - info 10-17-04 10:11:40,597 [1075298944] WARN root <> - info 10-17-04 10:11:40,597 [1075298944] ERROR root <> - info 10-17-04 10:11:40,598 [1075298944] FATAL root <> - info
warn-log and above allowed 10-17-04 10:11:40,598 [1075298944] WARN root <> - info 10-17-04 10:11:40,598 [1075298944] ERROR root <> - info 10-17-04 10:11:40,599 [1075298944] FATAL root <> - info
error-log and above allowed 10-17-04 10:11:40,599 [1075298944] ERROR root <> - info 10-17-04 10:11:40,600 [1075298944] FATAL root <> - info
fatal-log and above allowed 10-17-04 10:11:40,600 [1075298944] FATAL root <> - info
log disabled |
因爲log4cplus腳本配置中能夠設置日誌的級別、過濾器Filter,所以它也是進行輸出過濾的一種很好的選擇。腳本配置的使用具體參見第8節。
Loglog可使用setInternalDebugging()方法用來控制是否屏蔽輸出信息中的調試信息,當輸入參數爲false則屏蔽,缺省設置爲false。 另外方法setQuietMode()方法用來控制是否屏蔽全部輸出信息,當輸入參數爲true則屏蔽,缺省設置爲false。具體用法請參見4.2.5小節。
除了經過程序實現對log環境的配置以外,log4cplus經過PropertyConfigurator類實現了基於腳本配置的功能。經過腳本能夠完成對logger、appender和layout的配置,所以能夠解決怎樣輸出,輸出到哪裏的問題。
下面將簡單介紹一下腳本的語法規則,包括基本配置語法和高級配置語法。
基本配置語法主要針對包括rootLogger和non-root logger。
語法:log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...
語法:log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...
說明:INHERITED表示繼承父Logger的日誌級別。
語法:
log4cplus.appender.appenderName=fully.qualified.name.of.appender.class
fully.qualified.name.of.appeneder.class可用值:
log4cplus::ConsoleAppender |
終端輸出 |
log4cplus::FileAppender |
通常文件輸出 |
log4cplus::RollingFileAppender |
日誌大小輸出 |
log4cplus::DailyRollingFileAppender |
日期輸出 |
log4cplus::SocketAppender |
網絡端口輸出 |
文件通用選項:
選項 |
做用 |
ImmediateFlush |
是否當即刷新(默認爲true) |
log4cplus.appender.ALL_MSGS.ImmediateFlush=true |
|
File |
使用的文件名 |
log4cplus.appender.ALL_MSGS.File=all_msgs.log |
|
Append |
是否追加到以前的文件 |
log4cplus.appender.ALL_MSGS.Append=true |
|
ReopenDelay |
先將日誌緩存起來,等指定時間以後再往文件中插入 減小文件的保存次數 |
log4cplus.appender.ALL_MSGS.ReopenDelay=10【單位爲秒】 |
|
UseLockFile |
是否使用加鎖的方式去寫文件,默認是false |
log4cplus.appender.ALL_MSGS.UseLockFile=true |
|
LockFile |
使用的加鎖文件名 |
log4cplus.appender.ALL_MSGS.LockFile=fuck_are_you.lock[文件名沒有具體要求] |
|
Locale |
使用的字符集 |
log4cplus.appender.ALL_MSGS.Locale=chs【en,其餘參數具體見imbue參數】 |
|
Threshold |
指定日誌消息的輸出最低層次 |
log4cplus.appender.ALL_MSGS.Threshold=DEBUG |
DailyRollingFileAppender相關配置:
選項 |
做用 |
Schedule |
文件保存頻率 可選值:MONTHLY, WEEKLY, DAILY, TWICE_DAILY, HOURLY, MINUTELY |
log4cplus.appender.ALL_MSGS.Schedule=MINUTELY |
|
MaxBackupIndex |
最多文件個數 |
log4cplus.appender.ALL_MSGS. MaxBackupIndex=10 |
|
DatePattern |
指定文件名的日期格式 1)'.'yyyy-MM: 每個月 2)'.'yyyy-ww: 每週 3)'.'yyyy-MM-dd: 天天 4)'.'yyyy-MM-dd-a: 天天兩次 5)'.'yyyy-MM-dd-HH: 每小時 6)'.'yyyy-MM-dd-HH-mm: 每分鐘 |
log4cplus.appender.ALL_MSGS.DatePattern='.'yyyy-ww |
RollingFileAppender相關配置:
選項 |
做用 |
MaxFileSize |
最大文件大小,當小於200kb的時候,默認爲200kb,單位有(MB、KB) |
log4cplus.appender.ALL_MSGS. MaxFileSize=10 |
|
MaxBackupIndex |
最多文件個數 |
log4cplus.appender.ALL_MSGS. MaxBackupIndex=10 |
Appender能夠附加Filter組成的鏈表,若是Filter鏈中存在過濾器Filter, log4cplus在輸出日誌以前將調用鏈表中Filter的過濾方法decide(),根據該方法的返回值決定是否過濾該輸出日誌。
語法:
log4cplus.appender.appenderName.Filters.FilterNumber=fully.qualified.name.of.Filter.class
log4cplus.appender.appenderName.Filters.FilterNumber.FilterCondition=value.of.FilterCondition
log4cplus.appender.appenderName.Filters.AcceptOnMatch=true|false
舉例:
log4cplus.appender.append_1.filters.1=log4cplus::spi::LogLevelMatchFilter
log4cplus.appender.append_1.filters.1.LogLevelToMatch=TRACE
log4cplus.appender.append_1.filters.1.AcceptOnMatch=true
目前log4plus提供的過濾器包括DenyAllFilter 、LogLevelMatchFilter、LogLevelRangeFilter、和StringMatchFilter。
l LogLevelMatchFilter根據特定的日誌級別進行過濾。
過濾條件包括LogLevelToMatch和AcceptOnMatch(true|false), 只有當日志的LogLevel值與LogLevelToMatch相同,且AcceptOnMatch爲true時纔會匹配。
l LogLevelRangeFilter根據根據日誌級別的範圍進行過濾。
過濾條件包括LogLevelMin、LogLevelMax和AcceptOnMatch,只有當日志的LogLevel在LogLevelMin、LogLevelMax之間同時AcceptOnMatch爲true時纔會匹配。
l StringMatchFilter根據日誌內容是否包含特定字符串進行過濾。
過濾條件包括StringToMatch和AcceptOnMatch,只有當日志包含StringToMatch字符串 且AcceptOnMatch爲true時會匹配。
l DenyAllFilter則過濾掉全部消息。
過濾條件處理機制相似於Linux中IPTABLE的Responsibility chain機制,(即先deny、再allow)不過執行順序恰好相反,後寫的條件會被先執行,好比:
log4cplus.appender.append_1.filters.1=log4cplus::spi::LogLevelMatchFilter
log4cplus.appender.append_1.filters.1.LogLevelToMatch=TRACE
log4cplus.appender.append_1.filters.1.AcceptOnMatch=true
#log4cplus.appender.append_1.filters.2=log4cplus::spi::DenyAllFilter
會首先執行filters.2的過濾條件,關閉全部過濾器,而後執行filters.1,僅匹配TRACE信息。
能夠選擇不設置、TTCCLayout、或PatternLayout,若是不設置,會輸出SimpleLayout格式的日誌。
設置TTCCLayout的語法:log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout
設置PatternLayout的語法:log4cplus.appender.append_1.layout=log4cplus::PatternLayout
舉例:
log4cplus.appender.append_1.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p - %m%n
腳本方式使用起來很是簡單,只要首先加載配置便可(urconfig.properties是自行定義的配置文件):PropertyConfigurator::doConfigure("urconfig.properties");
下面咱們經過例子體會一下log4cplus強大的基於腳本過濾log信息的功能。如下是urconfig.properties示例腳本配置內容。
log4cplus.rootLogger=TRACE, ALL_MSGS, TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender log4cplus.appender.ALL_MSGS.File=all_msgs.log log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout
log4cplus.appender.TRACE_MSGS=log4cplus::RollingFileAppender log4cplus.appender.TRACE_MSGS.File=trace_msgs.log log4cplus.appender.TRACE_MSGS.layout=log4cplus::TTCCLayout log4cplus.appender.TRACE_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter log4cplus.appender.TRACE_MSGS.filters.1.LogLevelToMatch=TRACE log4cplus.appender.TRACE_MSGS.filters.1.AcceptOnMatch=true log4cplus.appender.TRACE_MSGS.filters.2=log4cplus::spi::DenyAllFilter
log4cplus.appender.DEBUG_INFO_MSGS=log4cplus::RollingFileAppender log4cplus.appender.DEBUG_INFO_MSGS.File=debug_info_msgs.log log4cplus.appender.DEBUG_INFO_MSGS.layout=log4cplus::TTCCLayout log4cplus.appender.DEBUG_INFO_MSGS.filters.1=log4cplus::spi::LogLevelRangeFilter log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMin=DEBUG log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMax=INFO log4cplus.appender.DEBUG_INFO_MSGS.filters.1.AcceptOnMatch=true log4cplus.appender.DEBUG_INFO_MSGS.filters.2=log4cplus::spi::DenyAllFilter
log4cplus.appender.FATAL_MSGS=log4cplus::RollingFileAppender log4cplus.appender.FATAL_MSGS.File=fatal_msgs.log log4cplus.appender.FATAL_MSGS.layout=log4cplus::TTCCLayout log4cplus.appender.FATAL_MSGS.filters.1=log4cplus::spi::StringMatchFilter log4cplus.appender.FATAL_MSGS.filters.1.StringToMatch=FATAL log4cplus.appender.FATAL_MSGS.filters.1.AcceptOnMatch=true log4cplus.appender.FATAL_MSGS.filters.2=log4cplus::spi::DenyAllFilter |
如下是示例代碼。
#include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <log4cplus/helpers/stringhelper.h> #include <log4cplus/loggingmacros.h> #include <iostream> #include <string>
using namespace std; using namespace log4cplus; using namespace log4cplus::helpers; using namespace log4cplus::thread;
static Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("log")); void printDebug() { LOG4CPLUS_TRACE_METHOD(logger, LOG4CPLUS_TEXT("::printDebug()")); LOG4CPLUS_DEBUG(logger, "This is a DEBUG message"); LOG4CPLUS_INFO(logger, "This is a INFO message"); LOG4CPLUS_WARN(logger, "This is a WARN message"); LOG4CPLUS_ERROR(logger, "This is a ERROR message"); LOG4CPLUS_FATAL(logger, "This is a FATAL message"); }
int main() { Logger root = Logger::getRoot(); PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("urconfig.properties")); printDebug();
return 0; } |
輸出結果:
1. all_msgs.log 10-17-04 14:55:25,858 [1075298944] TRACE log <> - ENTER: ::printDebug() 10-17-04 14:55:25,871 [1075298944] DEBUG log <> - This is a DEBUG message 10-17-04 14:55:25,873 [1075298944] INFO log <> - This is a INFO message 10-17-04 14:55:25,873 [1075298944] WARN log <> - This is a WARN message 10-17-04 14:55:25,874 [1075298944] ERROR log <> - This is a ERROR message 10-17-04 14:55:25,874 [1075298944] FATAL log <> - This is a FATAL message 10-17-04 14:55:25,875 [1075298944] TRACE log <> - EXIT:::printDebug()
2. trace_msgs.log 10-17-04 14:55:25,858 [1075298944] TRACE log <> - ENTER: ::printDebug() 10-17-04 14:55:25,875 [1075298944] TRACE log <> - EXIT:::printDebug()
3. debug_info_msgs.log 10-17-04 14:55:25,871 [1075298944] DEBUG log <> - This is a DEBUG message 10-17-04 14:55:25,873 [1075298944] INFO log <> - This is a INFO message
4. fatal_msgs.log 10-17-04 14:55:25,874 [1075298944] FATAL log <> - This is a FATAL message |
多線程版本的log4cplus提供了實用類ConfigureAndWatchThread,該類啓動線程對配置腳本進行監控,一旦發現配置腳本被更新則馬上從新加載配置。
類ConfigureAndWatchThread的構造函數定義爲:
ConfigureAndWatchThread(const log4cplus::tstring& propertyFile,
unsigned int millis = 60 * 1000);
第一個參數propertyFile爲配置腳本的路徑名,第二個參數爲監控時兩次更新檢查相隔的時間,單位爲耗秒ms。
#include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <log4cplus/helpers/loglog.h> #include <log4cplus/helpers/stringhelper.h> using namespace std; using namespace log4cplus; using namespace log4cplus::helpers;
Logger log_1 = Logger::getInstance("test.log_1"); Logger log_2 = Logger::getInstance("test.log_2"); Logger log_3 = Logger::getInstance("test.log_3");
void printMsgs(Logger& logger) { LOG4CPLUS_TRACE_METHOD(logger, "printMsgs()"); LOG4CPLUS_DEBUG(logger, "printMsgs()"); LOG4CPLUS_INFO(logger, "printMsgs()"); LOG4CPLUS_WARN(logger, "printMsgs()"); LOG4CPLUS_ERROR(logger, "printMsgs()"); }
int main() { cout << "Entering main()..." << endl; LogLog::getLogLog()->setInternalDebugging(true); Logger root = Logger::getRoot(); try { ConfigureAndWatchThread configureThread("log4cplus.properties", 5 * 1000); LOG4CPLUS_WARN(root, "Testing...."); for(int i=0; i<100; ++i) { printMsgs(log_1); printMsgs(log_2); printMsgs(log_3); log4cplus::helpers::sleep(1); } } catch(...) { cout << "Exception..." << endl; LOG4CPLUS_FATAL(root, "Exception occured...") } cout << "Exiting main()..." << endl; return 0; } |
如下是配置腳本log4cplus.properties的內容。
log4cplus.rootLogger=INFO, STDOUT, R log4cplus.logger.test=WARN log4cplus.logger.test.log_1=FATAL log4cplus.logger.test.log_2=FATAL log4cplus.logger.test.log_3=WARN
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.R=log4cplus::RollingFileAppender log4cplus.appender.R.File=output.log #log4cplus.appender.R.MaxFileSize=5MB log4cplus.appender.R.MaxFileSize=500KB log4cplus.appender.R.MaxBackupIndex=5 log4cplus.appender.R.layout=log4cplus::TTCCLayout |
log4cplus支持日誌級別的定製。若是須要定義本身的優先級,則能夠按如下步驟進行定製。
l 定義新日誌級別對應的常量整數和輸出宏。
/* * customloglevel.h */ #include <log4cplus/logger.h> #include <log4cplus/helpers/loglog.h>
using namespace log4cplus; using namespace log4cplus::helpers;
const LogLevel CRITICAL_LOG_LEVEL = 45000;
#define LOG4CPLUS_CRITICAL(logger, logEvent) \ if(logger.isEnabledFor(CRITICAL_LOG_LEVEL)) { \ log4cplus::tostringstream _log4cplus_buf; \ _log4cplus_buf << logEvent; \ logger.forcedLog(CRITICAL_LOG_LEVEL, _log4cplus_buf.str(), __FILE__,__LINE__); \ } |
l 定義新日誌級別對應的字符串、常量整數與字符串之間的轉換函數,定義本身的初始化器將轉換函數註冊到LogLevelManage。
#include "customloglevel.h" #define _CRITICAL_STRING "CRITICAL"
tstring criticalToStringMethod(LogLevel ll) { if(ll == CRITICAL_LOG_LEVEL) { return _CRITICAL_STRING; } else { return tstring(); } }
LogLevel criticalFromStringMethod(const tstring& s) { if(s == _CRITICAL_STRING) return CRITICAL_LOG_LEVEL; return NOT_SET_LOG_LEVEL; }
class CriticalLogLevelInitializer { public: CriticalLogLevelInitializer() { getLogLevelManager().pushToStringMethod(criticalToStringMethod); getLogLevelManager().pushFromStringMethod(criticalFromStringMethod); } };
CriticalLogLevelInitializer criticalLogLevelInitializer_; |
l 使用新定義的日誌級別
#include "customloglevel.h" #include <log4cplus/consoleappender.h> #include <iomanip> #include <iostream> using namespace std; using namespace log4cplus;
int main() { SharedAppenderPtr append_1(new ConsoleAppender()); append_1->setName("First"); Logger::getRoot().addAppender(append_1);
Logger root = Logger::getRoot(); LOG4CPLUS_CRITICAL(root, "This is a new logginglevel"); return 0; } |
LogLog輸出信息中老是包含"log4cplus:"前綴,這是由於LogLog在實現時候在構造函數中進行了硬編碼:
LogLog::LogLog() : mutex(LOG4CPLUS_MUTEX_CREATE), debugEnabled(false), quietMode(false), PREFIX( LOG4CPLUS_TEXT("log4cplus: ") ), WARN_PREFIX( LOG4CPLUS_TEXT("log4cplus:WARN ") ), ERR_PREFIX( LOG4CPLUS_TEXT("log4cplus:ERROR ") ) { } |
目錄[-]
log4cplus是C++編寫的開源的日誌系統,前身是java編寫的log4j系統,受Apache Software License保護,做者是Tad E. Smith。
log4cplus具備線程安全、靈活、以及多粒度控制的特色,經過將日誌劃分優先級使其能夠面向程序調試、運行、測試、和維護等全生命週期。你能夠選擇將日誌輸出到屏幕、文件、NT event log、甚至是遠程服務器;經過指定策略對日誌進行按期備份等等。
1- 解壓: gzip -cd log4cplus-x.x.x.tar.gz | tar -xf -
2- 進入log4cplus根目錄: cd log4cplus-x.x.x
3- 產生Makefile: ./configure --prefix=/where/to/install -enable-threads=no
若是須要指定安裝路徑可以使用--prefix參數, 不然將缺省安裝到/usr/local目錄下。另外,若是須要單線程版本可經過參數-enable-threads=no指定, 不然默認將安裝多線程版本。
對於HP-UNIX平臺用戶, 因爲aCC編譯器選項兼容性問題,請另外加入參數CXXFLAGS=」-AA -w」(單線程版本)或CXXFLAGS=」-AA –mt -w」(多線程版本)。
4- 建立: make
對於HP-UNIX用戶,因爲aCC編譯器不包含-Wall選項來顯示全部警告,建立時將致使無效的-W參數錯誤,請修改/log4cplus-x.x.x/src目錄下的Makefile,將AM_CPPFLAGS = -Wall 行的-Wall選項刪除或註釋掉。
此外,某些HP-UNIX平臺的套接字鏈接接受函數accept()第三個參數要求爲int*,而在socket-unix.cxx源文件153行實現中實際傳入的是socklen_t*類型,平臺並不支持,也將致使編譯錯誤。解決方法是將源代碼該行中的傳入參數強制轉換爲int*類型便可。
注意AIX和Linux平臺目前並無上述兩處建立錯誤。
對於AIX平臺用戶請保證建立時使用的編譯器是xlC而不是g++,不然將致使log4cplus腳本配置功能運行時產生段異常,生成core文件。有鑑於此,也請保證HP-UNIX用戶儘可能使用aCC編譯器進行建立。
5- 建立/log4cplus/tests目錄下的測試用例: make check
6- 安裝: make install
安裝成功後將在/usr/local目錄或指定的目錄下建立include和lib兩個子目錄及相應文件。其中include目錄包含頭文件,lib目錄包含最終打包生成的靜態和動態庫。在動態鏈接log4cplus庫時請使用-llog4cplus選項。
類名 |
說明 |
Filter |
過濾器,過濾輸出消息。過濾器,解決哪些信息須要輸出的問題,好比DEBUG,WARR,INFO等的輸出控制 |
Layout |
佈局器,控制輸出消息的格式。格式化輸出信息,解決了如何輸出的問題。 |
Appender |
掛接器,與佈局器和過濾器緊密配合,將特定格式的消息過濾後輸出到所掛接的設備終端如屏幕,文件等等)。接收日誌的各個設備,如控制檯、文件、網絡等。解決了輸出到哪裏去的問題 |
Logger |
記錄器,保存並跟蹤對象日誌信息變動的實體,當你須要對一個對象進行記錄時,就須要生成一個logger。日誌模塊,程序中惟一一個必須得使用的模塊,解決了在哪裏使用日誌的問題。 |
Hierarchy |
分類器,層次化的樹型結構,用於對被記錄信息的分類,層次中每個節點維護一個logger的全部信息。 |
LogLevel |
優先權,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。 |
使用log4cplus有六個基本步驟:
l 實例化一個封裝了輸出介質的appender對象;
l 實例化一個封裝了輸出格式的layout對象;
l 將layout對象綁定(attach)到appender對象;如省略此步驟,簡單佈局器SimpleLayout(參見5.1小節)對象會綁定到logger。
l 實例化一個封裝了日誌輸出logger對象,並調用其靜態函數getInstance()得到實例,log4cplus::Logger::getInstance("logger_name");
l 將appender對象綁定(attach)到logger對象;
l 設置logger的優先級,如省略此步驟,各類有限級的日誌都將被輸出。
下面經過一些例子來了解log4cplus的基本使用。
/* *標準使用,嚴格實現步驟1-6。 */ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h> #include <log4cplus/layout.h>
using namespace log4cplus; using namespace log4cplus::helpers;
int main() { /* step 1: Instantiate an appender object */ SharedObjectPtr<Appender> _append (new ConsoleAppender()); _append->setName("append for test");
/* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr<Layout> _layout(new PatternLayout(pattern));
/* step 3: Attach the layout object to the appender */ _append->setLayout( _layout );
/* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test");
/* step 5: Attach the appender object to the logger */ _logger.addAppender(_append);
/* step 6: Set a priority for the logger */ _logger.setLogLevel(ALL_LOG_LEVEL);
/* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") return 0; } |
輸出結果:
10/14/04 09:06:24 - This is the FIRST log message... [main.cpp:31] 10/14/04 09:06:25 - This is the SECOND log message... [main.cpp:33] |
/* *簡潔使用,僅實現步驟1、4、5。 */ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h>
using namespace log4cplus; using namespace log4cplus::helpers;
int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test");
/* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test");
/* step 5: Attach the appender object to the logger */ _logger.addAppender(_append);
/* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...")
return 0; } |
輸出結果:
DEBUG - This is the FIRST log message... WARN - This is the SECOND log message... |
/* *iostream模式,appender輸出到控制檯。 */ #include<log4cplus/logger.h> #include<log4cplus/consoleappender.h> #include<iomanip> usingnamespacelog4cplus;
int main() { /*step1:Instantiateanappenderobject*/ SharedAppenderPtr_append(new ConsoleAppender()); _append->setName("appendtest");
/*step4:Instantiatealoggerobject*/ Logger_logger = Logger::getInstance("test");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ LOG4CPLUS_TRACE(_logger, "Thisis" << "justat" << "est." << std::endl) LOG4CPLUS_DEBUG(_logger, "Thisisabool:" << true) LOG4CPLUS_INFO(_logger, "Thisisachar:" << 'x') LOG4CPLUS_WARN(_logger, "Thisisaint:" << 1000) LOG4CPLUS_ERROR(_logger, "Thisisalong(hex):" << std::hex << 100000000) LOG4CPLUS_FATAL(_logger, "Thisisadouble:" << std::setprecision(15) << 1.2345234234)
return0; } |
輸出結果:
DEBUG-Thisisabool:1 INFO-Thisisachar:x WARN-Thisisaint:1000 ERROR-Thisisalong(hex):5f5e100 FATAL-Thisisadouble:1.2345234234 |
/* *文件模式,appender輸出到文件。 */ #include<log4cplus/logger.h> #include<log4cplus/fileappender.h> usingnamespacelog4cplus;
int main() { /*step1:Instantiateanappenderobject*/ SharedAppenderPtr_append(new FileAppender("Test.log")); _append->setName("filelogtest");
/*step4:Instantiatealoggerobject*/ Logger_logger = Logger::getInstance("test.subtestof_filelog");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ for (int i = 0; i < 5; ++i) { LOG4CPLUS_DEBUG(_logger, "Enteringloop#" << i << "Endline#") }
return0; } |
輸出結果(Test.log文件):
DEBUG-Enteringloop#0Endline# DEBUG-Enteringloop#1Endline# DEBUG-Enteringloop#2Endline# DEBUG-Enteringloop#3Endline# DEBUG-Enteringloop#4Endline# |
LogLog類實現了debug, warn, error 函數用於logcplus運行時顯示log4cplus自身的調試、警告或錯誤信息,是對標準輸出的簡單封裝,它也能夠用來進行簡單的日誌輸出。LogLog 同時提供了兩個方法來進一步控制所輸出的信息,其中setInternalDebugging()方法用來控制是否屏蔽輸出信息中的調試信息,當輸入參數爲false則屏蔽,缺省設置爲false。 setQuietMode()方法用來控制是否屏蔽全部輸出信息,當輸入參數爲true則屏蔽,缺省設置爲false。
/* 經過loglog來控制輸出調試、警告或錯誤信息,appender輸出到屏幕。 */ #include <iostream> #include <log4cplus/helpers/loglog.h> using namespace log4cplus::helpers;
void printMsgs(void) { std::cout << "Entering printMsgs()..." << std::endl; LogLog::getLogLog()->debug("This is a Debug statement..."); LogLog::getLogLog()->warn("This is a Warning..."); LogLog::getLogLog()->error("This is a Error..."); std::cout << "Exiting printMsgs()..." << std::endl << std::endl; }
int main() { printMsgs(); std::cout << "Turning on debug..." << std::endl; LogLog::getLogLog()->setInternalDebugging(true); printMsgs(); std::cout << "Turning on quiet mode..." << std::endl; LogLog::getLogLog()->setQuietMode(true); printMsgs(); return 0; } |
輸出結果:
EnteringprintMsgs()... log4cplus:WARNThisisaWarning... log4cplus:ERRORThisisaError... ExitingprintMsgs()... Turningondebug... EnteringprintMsgs()... log4cplus:ThisisaDebugstatement... log4cplus:WARNThisisaWarning... log4cplus:ERRORThisisaError... ExitingprintMsgs()... Turningonquietmode... EnteringprintMsgs()... ExitingprintMsgs()... |
注意輸出信息中老是包含"log4cplus:"前綴,若是須要定製使其使用其餘的前綴請參見9.2小節。
log4cplus在頭文件loggingmacros.h中提供瞭如下的日誌輸出宏:
LOG4CPLUS_TRACE_METHOD(logger,logEvent)
LOG4CPLUS_TRACE(logger,logEvent) LOG4CPLUS_TRACE_STR(logger,logEvent)
LOG4CPLUS_DEBUG(logger,logEvent) LOG4CPLUS_DEBUG_STR(logger,logEvent)
LOG4CPLUS_INFO(logger,logEvent) LOG4CPLUS_INFO_STR(logger,logEvent)
LOG4CPLUS_WARN(logger,logEvent) LOG4CPLUS_WARN_STR(logger,logEvent)
LOG4CPLUS_ERROR(logger,logEvent) LOG4CPLUS_ERROR_STR(logger,logEvent)
LOG4CPLUS_FATAL(logger,logEvent) LOG4CPLUS_FATAL_STR(logger,logEvent) |
其中logger 爲Logger實例名稱,logEvent爲日誌內容。因爲log4cplus選用C++的流機制進行日誌輸出,所以爲了區分包含<<運算符和不包含<<運算符的日誌內容,分別提供了LOG4CPLUS_XXXX和LOG4CPLUS_XXXX_STR兩種日誌輸出宏。 另外,日誌輸出宏LOG4CPLUS_TRACE_METHOD主要用來跟蹤方法的調用軌跡。
log4cplus經過佈局器(Layouts)來控制輸出的格式,log4cplus提供了三種類型的Layouts,分別是SimpleLayout、PatternLayout、和TTCCLayout。
一種簡單格式的佈局器,在輸出的原始信息以前加上LogLevel和一個"-",若是初始化時沒有將佈局器附加到掛接器,則默認使用SimpleLayout。
如下代碼片斷演示瞭如何使用SimpleLayout。
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::auto_ptr<Layout> _layout(new log4cplus::SimpleLayout()); /* step 3: Attach the layout object to the appender */ _append->setLayout(_layout); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the simple formatted log message...") ... ... |
輸出結果:
DEBUG - This is the simple formatted log message... |
一種有詞法分析功能的模式佈局器,相似於C語言的printf()函數,可以對預約義的轉換標識符(conversion specifiers)進行解析,轉換成特定格式輸出。
如下代碼片斷演示瞭如何使用PatternLayout。
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr _append (new ConsoleAppender()); _append->setName("append for test");
/* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr<Layout> _layout(new PatternLayout(pattern));
/* step 3: Attach the layout object to the appender */ _append->setLayout(_layout);
/* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test_logger.subtest");
/* step 5: Attach the appender object to the logger */ _logger.addAppender(_append);
/* log activity */ LOG4CPLUS_DEBUG(_logger, "teststr") ... ... |
輸出結果:
10/16/04 18:51:25 - teststr [main.cpp:51] |
PatterLayout支持的轉換標識符主要包括:
(1)"%%",轉義爲%, 即,std::string pattern = "%%" 時輸出"%"。
(2)"%c",輸出logger名稱,好比std::string pattern ="%c" 時輸出: "test_logger.subtest", 也能夠控制logger名稱的顯示層次,好比"%c{1}"時輸出"test_logger",其中數字表示層次。
(3)"%D",顯示本地時間,當std::string pattern ="%D" 時輸出:"2004-10-16 18:55:45",%d顯示標準時間,因此當std::string pattern ="%d" 時輸出"2004-10-16 10:55:45" (由於北京時間位於東8區,差8個小時)。
能夠經過%d{...}定義更詳細的顯示格式,好比%d{%H:%M:%s}表示要顯示小時:分鐘:秒。大括號中可顯示的預約義標識符以下:
%a -- 表示禮拜幾,英文縮寫形式,好比"Fri"
%A -- 表示禮拜幾,好比"Friday"
%b -- 表示幾月份,英文縮寫形式,好比"Oct"
%B -- 表示幾月份,"October"
%c -- 標準的日期+時間格式,如 "Sat Oct 16 18:56:19 2004"
%d -- 表示今天是這個月的幾號(1-31)"16"
%H -- 表示當前時刻是幾時(0-23),如 "18"
%I -- 表示當前時刻是幾時(1-12),如 "6"
%j -- 表示今天是哪一天(1-366),如 "290"
%m -- 表示本月是哪一月(1-12),如 "10"
%M -- 表示當前時刻是哪一分鐘(0-59),如 "59"
%p -- 表示如今是上午仍是下午, AM or PM
%q -- 表示當前時刻中毫秒部分(0-999),如 "237"
%Q -- 表示當前時刻中帶小數的毫秒部分(0-999.999),如 "430.732"
%S -- 表示當前時刻的多少秒(0-59),如 "32"
%U -- 表示本週是今年的第幾個禮拜,以週日爲第一天開始計算(0-53),如 "41"
%w -- 表示禮拜幾,(0-6, 禮拜天爲0),如 "6"
%W -- 表示本週是今年的第幾個禮拜,以週一爲第一天開始計算(0-53),如 "41"
%x -- 標準的日期格式,如 "10/16/04"
%X -- 標準的時間格式,如 "19:02:34"
%y -- 兩位數的年份(0-99),如 "04"
%Y -- 四位數的年份,如 "2004"
%Z -- 時區名,好比 "GMT"
(4)"%F",輸出當前記錄器所在的文件名稱,好比std::string pattern ="%F" 時輸出: "main.cpp"。
(5)"%L",輸出當前記錄器所在的文件行號,好比std::string pattern ="%L" 時輸出: "51"
(6)"%l",輸出當前記錄器所在的文件名稱和行號,好比std::string pattern ="%l" 時輸出"main.cpp:51"。
(7)"%m",輸出原始信息,好比std::string pattern ="%m" 時輸出: "teststr",即上述代碼中LOG4CPLUS_DEBUG的第二個參數,這種實現機制能夠確保原始信息被嵌入到帶格式的信息中。
(8)"%n",換行符,沒什麼好解釋的。
(9)"%p",輸出LogLevel,好比std::string pattern ="%p" 時輸出: "DEBUG"。
(10)"%t",輸出記錄器所在的線程ID,好比std::string pattern ="%t" 時輸出: "1075298944"。
(11)"%x",嵌套診斷上下文NDC (nested diagnostic context) 輸出,從堆棧中彈出上下文信息,NDC能夠用對不一樣源的log信息(同時地)交叉輸出進行區分,關於NDC方面的詳細介紹會在下文中提到。
(12)格式對齊,好比std::string pattern ="%-10m"時表示左對齊,寬度是10,此時會輸出"teststr ",固然其它的控制字符也能夠相同的方式來使用,好比"%-12d","%-5p"等等。
是在PatternLayout基礎上發展的一種缺省的帶格式輸出的佈局器,其格式由時間,線程ID,Logger和NDC 組成(consists of time, thread, Logger and nested diagnostic context information, hence the name),於是得名, 關於NDC請參見6.4小節。
如下代碼片斷演示瞭如何使用TTCCLayout。
...... /*step1:Instantiateanappenderobject*/ SharedObjectPtr_append(new ConsoleAppender()); _append->setName("appendfortest");
/*step2:Instantiatealayoutobject*/ std::auto_ptr_layout(new TTCCLayout());
/*step3:Attachthelayoutobjecttotheappender*/ _append->setLayout(_layout);
/*step4:Instantiatealoggerobject*/ Logger_logger=Logger::getInstance("test_logger");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ LOG4CPLUS_DEBUG(_logger,"teststr") ...... |
輸出結果:
10-16-04 19:08:27,501 [1075298944] DEBUG test_logger <> - teststr |
TTCCLayout在構造時,有機會選擇顯示本地時間或GMT時間,缺省是按照本地時間顯示:TTCCLayout::TTCCLayout(bool use_gmtime = false)。
若是須要構造TTCCLayout對象時選擇GMT時間格式,則使用方式以下代碼片段所示。
... ... /* step 2: Instantiate a layout object */ std::auto_ptr _layout(new TTCCLayout(true)); ... ... |
輸出結果:
10-16-04 11:12:47,678 [1075298944] DEBUG test_logger <> - teststr |
log4cplus默認將輸出到控制檯,提供ConsoleAppender用於操做。示例代碼請參見4.2.1、4.2.2或4.2.3小節,這裏再也不贅述。
log4cplus提供了三個類用於文件操做,它們是FileAppender類、RollingFileAppender類、DailyRollingFileAppender類。
實現了基本的文件操做功能,構造函數以下:
FileAppender ::FileAppender(const log4cplus::tstring& filename,
LOG4CPLUS_OPEN_MODE_TYPE mode =
LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc,
bool immediateFlush = true);
filename : 文件名
mode : 文件類型,可選擇的文件類型包括app、ate、binary、in、out、trunc,由於實際上只是對stl的一個簡單包裝,這裏就很少講了。缺省是trunc,表示將先前文件刪除。
immediateFlush : 緩衝刷新標誌,若是爲true表示每向文件寫一條記錄就刷新一次緩存,不然直到FileAppender被關閉或文件緩存已滿才更新文件,通常是要設置true的,好比你往文件寫的過程當中出現了錯誤(如程序非正常退出),即便文件沒有正常關閉也能夠保證程序終止時刻以前的全部記錄都會被正常保存。
FileAppender類的使用狀況請參考4.2.5小節,這裏再也不贅述。
實現能夠滾動轉儲的文件操做功能,構造函數以下:
RollingFileAppender::RollingFileAppender(const log4cplus::tstring& filename,
long maxFileSize,
int maxBackupIndex,
bool immediateFlush)
filename : 文件名
maxFileSize : 文件的最大尺寸
maxBackupIndex : 最大記錄文件數
immediateFlush : 緩衝刷新標誌
RollingFileAppender類能夠根據你預先設定的大小來決定是否轉儲,當超過該大小,後續log信息會另存到新文件中,除了定義每一個記錄文件的大小以外,你還要肯定在RollingFileAppender類對象構造時最多須要多少個這樣的記錄文件(maxBackupIndex+1),當存儲的文件數目超過maxBackupIndex+1時,會刪除最先生成的文件,保證整個文件數目等於maxBackupIndex+1。而後繼續記錄,好比如下代碼片斷:
... ... #define LOOP_COUNT 200000 SharedAppenderPtr _append(new RollingFileAppender("Test.log", 5*1024, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr(new TTCCLayout()) ); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot(); Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); for(int i=0; i < LOOP_COUNT; ++i) { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ... |
輸出結果:
運行後會產生6個輸出文件,Test.log、Test.log.1、Test.log.2、Test.log.3、Test.log.4、Test.log.5其中Test.log存放着最新寫入的信息,而最後一個文件中並不包含第一個寫入信息,說明已經被不斷更新了。
須要指出的是,這裏除了Test.log以外,每一個文件的大小都是200K,而不是咱們想像中的5K,這是由於log4cplus中隱含定義了文件的最小尺寸是200K,只有大於200K的設置才生效,<= 200k的設置都會被認爲是200K。
實現根據頻度來決定是否轉儲的文件轉儲功能,構造函數以下:
DailyRollingFileAppender::DailyRollingFileAppender(const log4cplus::tstring& filename,
DailyRollingFileSchedule schedule,
bool immediateFlush,
int maxBackupIndex)
filename : 文件名
schedule : 存儲頻度
immediateFlush : 緩衝刷新標誌
maxBackupIndex : 最大記錄文件數
DailyRollingFileAppender類能夠根據你預先設定的頻度來決定是否轉儲,當超過該頻度,後續log信息會另存到新文件中,這裏的頻度包括:MONTHLY(每個月)、WEEKLY(每週)、DAILY(每日)、TWICE_DAILY(每兩天)、HOURLY(每時)、MINUTELY(每分)。maxBackupIndex的含義同上所述,好比如下代碼片斷:
... ... SharedAppenderPtr _append(new DailyRollingFileAppender("Test.log", MINUTELY, true, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr(new TTCCLayout()) ); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot(); Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); for(int i=0; i < LOOP_COUNT; ++i) { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ... |
輸出結果:
運行後會以分鐘爲單位,分別生成名爲Test.log.2004-10-17-03-03、Test.log.2004-10-17-03-04和Test.log.2004-10-17-03-05這樣的文件。
須要指出的是這裏的"頻度"並非你寫入文件的速度,實際上是否轉儲的標準並不依賴你寫入文件的速度,而是依賴於寫入的那一時刻是否知足了頻度條件,便是否超過了以分鐘、小時、周、月爲單位的時間刻度,若是超過了就另存。
log4cplus提供了SocketAppender,實現了C/S方式的日誌記錄,用於支持重定向到遠程服務器。
(1) 定義一個SocketAppender類型的掛接器
SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));
(2) 把該掛接器加入到logger中
Logger::getRoot().addAppender(_append);
(3) SocketAppender類型不須要Layout, 直接調用宏就能夠將信息發往loggerServer了LOG4CPLUS_INFO(Logger::getRoot(), "This is a test: ")
注意這裏對宏的調用實際上是調用了SocketAppender::append(),裏面有一個數據傳輸約定,即先發送一個後續數據的總長度,而後再發送實際的數據:
... ... SocketBuffer buffer = convertToBuffer(event, serverName); SocketBuffer msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE); msgBuffer.appendSize_t(buffer.getSize()); msgBuffer.appendBuffer(buffer); ... ... |
(1) 定義一個ServerSocket
ServerSocket serverSocket(port);
(2) 調用accept函數建立一個新的socket與客戶端鏈接
Socket sock = serverSocket.accept();
(3) 此後便可用該sock進行數據read/write了,形如(完整代碼見6.3.3小節):
SocketBuffer msgSizeBuffer(sizeof(unsigned int));
if(!clientsock.read(msgSizeBuffer)){
return;
}
unsigned int msgSize = msgSizeBuffer.readInt();
SocketBuffer buffer(msgSize);
if(!clientsock.read(buffer)){
return;
}
(4) 爲了將讀到的數據正常顯示出來,須要將SocketBuffer存放的內容轉換成InternalLoggingEvent格式:
log4cplus::spi::InternalLoggingEvent event = readFromBuffer(buffer);
而後輸出:
Logger logger = Logger::getInstance(event.getLoggerName());
logger.callAppenders(event);
注意read/write是按照阻塞方式實現的,意味着對其調用直到知足了所接收或發送的個數才返回。
如下是服務器端代碼。
#include <log4cplus/config.h> #include <log4cplus/configurator.h> #include <log4cplus/consoleappender.h> #include <log4cplus/socketappender.h> #include <log4cplus/helpers/loglog.h> #include <log4cplus/helpers/socket.h> #include <log4cplus/helpers/threads.h> #include <log4cplus/spi/loggerimpl.h> #include <log4cplus/spi/loggingevent.h> #include <iostream> using namespace std; using namespace log4cplus; using namespace log4cplus::helpers; using namespace log4cplus::thread;
namespace loggingserver { class ClientThread : public AbstractThread { public: ClientThread(Socket clientsock) : clientsock(clientsock) { cout << "Received a client connection!!!!" << endl; }
~ClientThread() { cout << "Client connection closed." << endl; }
virtual void run();
private: Socket clientsock; };
}
int main(int argc, char** argv) { if (argc < 3) { cout << "Usage: port config_file" << endl; return 1; } int port = atoi(argv[1]); tstring configFile = LOG4CPLUS_C_STR_TO_TSTRING(argv[2]);
PropertyConfigurator config(configFile); config.configure();
ServerSocket serverSocket(port); 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; } SocketBuffer msgSizeBuffer(sizeof(unsigned int)); if (!clientsock.read(msgSizeBuffer)) { return; } unsigned int msgSize = msgSizeBuffer.readInt(); SocketBuffer buffer(msgSize); if (!clientsock.read(buffer)) { return; } spi::InternalLoggingEvent event = readFromBuffer(buffer); Logger logger = Logger::getInstance(event.getLoggerName()); logger.callAppenders(event); } } |
如下是客戶端代碼。
#include <log4cplus/logger.h> #include <log4cplus/socketappender.h> #include <log4cplus/loglevel.h> #include <log4cplus/tstring.h> #include <log4cplus/helpers/threads.h> #include <iomanip>
using namespace std; using namespace log4cplus;
int main(int argc, char **argv) { log4cplus::helpers::sleep(1); tstring serverName = (argc > 1 ? LOG4CPLUS_C_STR_TO_TSTRING(argv[1]) : tstring()); //tstring host = LOG4CPLUS_TEXT("192.168.2.10"); tstring host = LOG4CPLUS_TEXT("127.0.0.1"); SharedAppenderPtr append_1(new SocketAppender(host, 9998, serverName)); append_1->setName( LOG4CPLUS_TEXT("First") ); Logger::getRoot().addAppender(append_1);
Logger root = Logger::getRoot(); Logger test = Logger::getInstance( LOG4CPLUS_TEXT("socket.test") );
LOG4CPLUS_DEBUG(root, "This is" << " a reall" << "y long message." << endl << "Just testing it out" << endl << "What do you think?") test.setLogLevel(NOT_SET_LOG_LEVEL); LOG4CPLUS_DEBUG(test, "This is a bool: " << true) LOG4CPLUS_INFO(test, "This is a char: " << 'x') LOG4CPLUS_INFO(test, "This is a short: " << (short)-100) LOG4CPLUS_INFO(test, "This is a unsigned short: " << (unsigned short)100) log4cplus::helpers::sleep(0, 500000); LOG4CPLUS_INFO(test, "This is a int: " << (int)1000) LOG4CPLUS_INFO(test, "This is a unsigned int: " << (unsigned int)1000) LOG4CPLUS_INFO(test, "This is a long(hex): " << hex << (long)100000000) LOG4CPLUS_INFO(test, "This is a unsigned long: " << (unsigned long)100000000) LOG4CPLUS_WARN(test, "This is a float: " << (float)1.2345) LOG4CPLUS_ERROR(test, "This is a double: " << setprecision(15) << (double)1.2345234234) LOG4CPLUS_FATAL(test, "This is a long double: " << setprecision(15) << (long double)123452342342.342)
return 0; } |
log4cplus中的嵌入診斷上下文(Nested Diagnostic Context),即NDC。對log系統而言,當輸入源可能不止一個,而只有一個輸出時,每每須要分辯所要輸出消息的來源,好比服務器處理來自不一樣客戶端的消息時就須要做此判斷,NDC能夠爲交錯顯示的信息打上一個標記(stamp),使得辨認工做看起來比較容易些。這個標記是線程特有的,利用了線程局部存儲機制,稱爲線程私有數據(Thread-Specific Data,或TSD)。相關定義以下,包括定義、初始化、獲取、設置和清除操做:
linux pthread #define LOG4CPLUS_THREAD_LOCAL_TYPE pthread_key_t* #define LOG4CPLUS_THREAD_LOCAL_INIT ::log4cplus::thread::createPthreadKey() #define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) pthread_getspecific(*key) #defineLOG4CPLUS_SET_THREAD_LOCAL_VALUE(key,value) \ pthread_setspecific(*key, value) #define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) pthread_key_delete(*key)
win32 #define LOG4CPLUS_THREAD_LOCAL_TYPE DWORD #define LOG4CPLUS_THREAD_LOCAL_INIT TlsAlloc() #define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) TlsGetValue(key) #define LOG4CPLUS_SET_THREAD_LOCAL_VALUE( key, value ) \ TlsSetValue(key, static_cast(value)) #define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) TlsFree(key) |
使用起來比較簡單,在某個線程中:
NDC& ndc = log4cplus::getNDC(); ndc.push("ur ndc string"); LOG4CPLUS_DEBUG(logger, "this is a NDC test"); ... ... ndc.pop(); ... ... LOG4CPLUS_DEBUG(logger, "There should be no NDC..."); ndc.remove(); |
輸出結果(當設定輸出格式爲TTCCLayout時):
10-21-04 21:32:58, [3392] DEBUG test - this is a NDC test 10-21-04 21:32:58, [3392] DEBUG test <> - There should be no NDC... |
也能夠在自定義的輸出格式中使用NDC(用%x) ,好比:
... ... std::string pattern = "NDC:[%x] - %m %n"; std::auto_ptr _layout(new PatternLayout(pattern)); ... ... LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") NDC& ndc = log4cplus::getNDC(); ndc.push("ur ndc string"); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") ndc.pop(); ndc.remove(); ... ... |
輸出結果:
NDC:[] - This is the FIRST log message... NDC:[ur ndc string] - This is the SECOND log message... |
另一種更簡單的使用方法是在線程中直接用NDCContextCreator:
NDCContextCreator _first_ndc("ur ndc string");
G4CPLUS_DEBUG(logger, "this is a NDC test")
沒必要顯式地調用push/pop了,並且當出現異常時,可以確保push與pop的調用是匹配的。
log4cplus將輸出的log信息按照LogLevel(從低到高)分爲:
級別 |
說明 |
NOT_SET_LOG_LEVEL ( -1) |
接受缺省的LogLevel,若是有父logger則繼承它的LogLevel |
ALL_LOG_LEVEL (0) |
開放全部log信息輸出 |
TRACE_LOG_LEVEL (0) |
開放trace信息輸出(即ALL_LOG_LEVEL) |
DEBUG_LOG_LEVEL(10000) |
開放debug信息輸出 |
INFO_LOG_LEVEL (20000) |
開放info信息輸出 |
WARN_LOG_LEVEL (30000) |
開放warning信息輸出 |
ERROR_LOG_LEVEL(40000) |
開放error信息輸出 |
FATAL_LOG_LEVEL (50000) |
開放fatal信息輸出 |
OFF_LOG_LEVEL (60000) |
關閉全部log信息輸出 |
在log4cplus中,全部logger都經過一個層次化的結構(其實內部是hash表)來組織的,有一個Root級別的logger,能夠經過如下方法獲取:Logger root = Logger::getRoot();
用戶定義的logger都有一個名字與之對應,好比:Logger test = Logger::getInstance("test");
能夠定義該logger的子logger: Logger subTest = Logger::getInstance("test.subtest");
注意Root級別的logger只有經過getRoot方法獲取,Logger::getInstance("root")得到的是它的子對象而已。有了這些具備父子關係的logger以後可分別設置其LogLevel,好比:
root.setLogLevel( ... );
Test.setLogLevel( ... );
subTest.setLogLevel( ... );
各個logger能夠經過setLogLevel設置本身的優先級,當某個logger的LogLevel設置成NOT_SET_LOG_LEVEL時,該logger會繼承父logger的優先級,另外,若是定義了重名的多個logger, 對其中任何一個的修改都會同時改變其它logger。
log4cplus支持編譯時候和運行時刻利用日誌級別進行輸出過濾。編譯時刻經過以下的預約義變量進行過濾:
#define LOG4CPLUS_DISABLE_FATAL #define LOG4CPLUS_DISABLE_WARN #define LOG4CPLUS_DISABLE_ERROR #define LOG4CPLUS_DISABLE_INFO #define LOG4CPLUS_DISABLE_DEBUG #define LOG4CPLUS_DISABLE_TRACE |
運行時刻的過濾則經過使用Logger的setLogLevel設置日誌級別進行過濾。
#include "log4cplus/logger.h" #include "log4cplus/consoleappender.h" #include "log4cplus/loglevel.h" #include <iostream> using namespace std; using namespace log4cplus;
int main() { SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("test"); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot();
Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); LogLevelManager& llm = getLogLevelManager();
cout << endl << "Before Setting, Default LogLevel" << endl; LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root,"test.subtest:" << llm.toString(subTest.getChainedLogLevel()));
cout << endl << "Setting test.subtest to WARN" << endl; subTest.setLogLevel(WARN_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
cout << endl << "Setting test to TRACE" << endl; test.setLogLevel(TRACE_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
cout << endl << "Setting test.subtest to NO_LEVEL" << endl; subTest.setLogLevel(NOT_SET_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()) << '\n');
cout << "create a logger test_bak, named \"test_\", too. " << endl; Logger test_bak = Logger::getInstance("test"); cout << "Setting test to INFO, so test_bak also be set to INFO" << endl; test.setLogLevel(INFO_LOG_LEVEL); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test_bak: " << llm.toString(test_bak.getChainedLogLevel()));
return 0; } |
輸出結果:
Before Setting, Default LogLevel FATAL - root: DEBUG FATAL - test: DEBUG FATAL - test.subtest: DEBUG
Setting test.subtest to WARN FATAL - root: DEBUG FATAL - test: DEBUG FATAL - test.subtest: WARN
Setting test to TRACE FATAL - root: DEBUG FATAL - test: TRACE FATAL - test.subtest: WARN
Setting test.subtest to NO_LEVEL FATAL - root: DEBUG FATAL - test: TRACE FATAL - test.subtest: TRACE
create a logger test_bak, named "test_", too. Setting test to INFO, so test_bak also be set to INFO FATAL - test: INFO FATAL - test_bak: INFO |
#include "log4cplus/logger.h" #include |