Log4cplus使用指南


                             






Log4cplus使用指南





















目    錄
1 Log4cplus簡介    5
2 安裝方法    5
3 主要類說明    6
4 基本使用    6
4.1 基本步驟    6
4.2 使用示例    7
4.2.1 例1-標准使用    7
4.2.2 例2-簡潔使用    8
4.2.3 例3-輸出日志到控制台    9
4.2.4 例4-輸出日志到文件    10
4.2.5 例5-使用loglog輸出日志    11
4.3 日志輸出宏    13
5 輸出格式控制    14
5.1 SimpleLayout    14
5.2 PatternLayout    15
5.2.1 轉換標識符    15
5.3 TTCCLayout    17
6 輸出重定向    19
6.1 重定向到控制台    19
6.2 重定向到文件    19
6.2.1 FileAppender    19
6.2.2 RollingFileAppender    19
6.2.3 DailyRollingFileAppender    21
6.3 重定向到遠程服務器    22
6.3.1 客戶端程序需要做的工作    22
6.3.2 服務器端程序需要做的工作    22
6.3.3 例6-重定向到遠程服務器    23
6.4嵌入診斷上下文NDC    28
7 輸出過濾    30
7.1 利用日志級別進行輸出過濾    30
7.1.1 日志級別管理    30
7.1.2 利用日志級別進行輸出過濾    31
7.1.3 例7-日志的優先級    31
7.1.4 例8-運行時利用日志級別進行輸出過濾    34
7.2 利用腳本配置進行輸出過濾    37
7.3 LogLog的輸出過濾    37
8 腳本配置    37
8.1 基本配置    37
8.1.1根Logger的配置    37
8.1.2非根Logger的配置    37
8.2 高級配置    38
8.2.1 Appender配置    38
8.2.2 Filter配置    38
8.2.3 Layout配置    39
8.3.3 例9-腳本配置    39
8.3腳本配置的動態加載    42
8.3.1 例10-使用線程監控腳本的更新    42
9 定制Log4cplus    44
9.1 定制日志級別    44
9.2 定制LogLog    47
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
1 Log4cplus簡介
   log4cplus是C++編寫的開源的日志系統,前身是java編寫的log4j系統,受Apache Software License保護,作者是Tad E. Smith。
   log4cplus具有線程安全、靈活、以及多粒度控制的特點,通過將日志划分優先級使其可以面向程序調試、運行、測試、和維護等全生命周期。你可以選擇將日志輸出到屏幕、文件、NT event log、甚至是遠程服務器;通過指定策略對日志進行定期備份等等。
2 安裝方法
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選項。
3 主要類說明
類名    說明
Filter            過濾器,過濾輸出消息。
Layout          布局器,控制輸出消息的格式。
Appender       掛接器,與布局器和過濾器緊密配合,將特定格式的消息過濾后輸出到所掛接的設備終端如屏幕,文件等等)。
Logger           記錄器,保存並跟蹤對象日志信息變更的實體,當你需要對一個對象進行記錄時,就需要生成一個logger。
Hierarchy         分類器,層次化的樹型結構,用於對被記錄信息的分類,層次中每一個節點維護一個logger的所有信息。
LogLevel         優先權,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。
4 基本使用
4.1 基本步驟
   使用log4cplus有六個基本步驟:
 實例化一個封裝了輸出介質的appender對象;
 實例化一個封裝了輸出格式的layout對象;
 將layout對象綁定(attach)到appender對象;
   如省略此步驟,簡單布局器SimpleLayout(參見5.1小節)對象會綁定到logger。
 實例化一個封裝了日志輸出logger對象,並調用其靜態函數getInstance()獲得實 
  例,log4cplus::Logger::getInstance("logger_name");
 將appender對象綁定(attach)到logger對象;
設置logger的優先級,如省略此步驟,各種有限級的日志都將被輸出。
4.2 使用示例
   下面通過一些例子來了解log4cplus的基本使用。
4.2.1 例1-標准使用
/*
 *標准使用,嚴格實現步驟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]
4.2.2 例2-簡潔使用
/*
 *簡潔使用,僅實現步驟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...
4.2.3 例3-輸出日志到控制台
/*
 * iostream模式,appender輸出到控制台。
 */
#include <log4cplus/logger.h>
#include <log4cplus/consoleappender.h>
#include <iomanip>

using namespace log4cplus;

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_TRACE(_logger, "This is"  << " just a t" << "est." << std::endl)
    LOG4CPLUS_DEBUG(_logger, "This is a bool: " << true)
    LOG4CPLUS_INFO(_logger, "This is a char: " << 'x')
    LOG4CPLUS_WARN(_logger, "This is a int: " << 1000)
    LOG4CPLUS_ERROR(_logger, "This is a long(hex): " << std::hex << 100000000)
   LOG4CPLUS_FATAL(_logger, "This is a double: "  << std::setprecision(15)  << 1.2345234234)
   
    return 0;
}
輸出結果:
DEBUG - This is a bool: 1
INFO - This is a char: x
WARN - This is a int: 1000
ERROR - This is a long(hex): 5f5e100
FATAL - This is a double: 1.2345234234
4.2.4 例4-輸出日志到文件
/*
 *文件模式,appender輸出到文件。
 */
#include <log4cplus/logger.h>
#include <log4cplus/fileappender.h>

using namespace log4cplus;

int main()
{
    /* step 1: Instantiate an appender object */
    SharedAppenderPtr _append(new FileAppender("Test.log"));
   _append->setName("file log test");
   
    /* step 4: Instantiate a logger object */
   Logger _logger = Logger::getInstance("test.subtestof_filelog");
   
    /* step 5: Attach the appender object to the logger  */
   _logger.addAppender(_append);
   
    /* log activity */
    int i;
    for( i = 0; i < 5; ++i )
    {
        LOG4CPLUS_DEBUG(_logger, "Entering loop #" << i << "End line #")
    }

    return 0;
}
輸出結果(Test.log文件):
DEBUG - Entering loop #0End line #
DEBUG - Entering loop #1End line #
DEBUG - Entering loop #2End line #
DEBUG - Entering loop #3End line #
DEBUG - Entering loop #4End line #
4.2.5 例5-使用loglog輸出日志
   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;
}
輸出結果:
Entering printMsgs()...
log4cplus:WARN This is a Warning...
log4cplus:ERROR This is a Error...
Exiting printMsgs()...
Turning on debug...
Entering printMsgs()...
log4cplus: This is a Debug statement...
log4cplus:WARN This is a Warning...
log4cplus:ERROR This is a Error...
Exiting printMsgs()...
Turning on quiet mode...
Entering printMsgs()...
Exiting printMsgs()...
   注意輸出信息中總是包含"log4cplus:"前綴,如果需要定制使其使用其他的前綴請參見9.2小節。
4.3 日志輸出宏
    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主要用來跟蹤方法的調用軌跡。
5 輸出格式控制
   log4cplus通過布局器(Layouts)來控制輸出的格式,log4cplus提供了三種類型的Layouts,分別是SimpleLayout、PatternLayout、和TTCCLayout。
5.1 SimpleLayout
   一種簡單格式的布局器,在輸出的原始信息之前加上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...
5.2 PatternLayout
   一種有詞法分析功能的模式布局器,類似於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]
5.2.1 轉換標識符
   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"等等。
5.3 TTCCLayout
   是在PatternLayout基礎上發展的一種缺省的帶格式輸出的布局器, 其格式由時間,線程ID,Logger和NDC 組成(consists of time, thread, Logger and nested diagnostic context information, hence the name),因而得名, 關於NDC請參見6.4小節。
   以下代碼片段演示了如何使用TTCCLayout。   
   ... ...
    /* 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(new TTCCLayout());
   
    /* step 3: Attach the layout object to the appender */
   _append->setLayout( _layout );
   
    /* step 4: Instantiate a logger object */
   Logger _logger = Logger::getInstance("test_logger");
   
    /* step 5: Attach the appender object to the logger  */
   _logger.addAppender(_append);
   
     /* log activity */
    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
6 輸出重定向
6.1 重定向到控制台
    log4cplus默認將輸出到控制台,提供ConsoleAppender用於操作。示例代碼請參見4.2.1、4.2.2或4.2.3小節,這里不再贅述。
6.2 重定向到文件
   log4cplus提供了三個類用於文件操作,它們是FileAppender類、RollingFileAppender類、DailyRollingFileAppender類。
6.2.1 FileAppender
   實現了基本的文件操作功能,構造函數如下:
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小節,這里不再贅述。
6.2.2 RollingFileAppender
  實現可以滾動轉儲的文件操作功能,構造函數如下:
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    {
        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。
6.2.3 DailyRollingFileAppender
   實現根據頻度來決定是否轉儲的文件轉儲功能,構造函數如下:
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    {
        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這樣的文件。
   需要指出的是這里的"頻度"並不是你寫入文件的速度,其實是否轉儲的標准並不依賴你寫入文件的速度,而是依賴於寫入的那一時刻是否滿足了頻度條件,即是否超過了以分鍾、小時、周、月為單位的時間刻度,如果超過了就另存。
6.3 重定向到遠程服務器
   log4cplus提供了SocketAppender,實現了C/S方式的日志記錄,用於支持重定向到遠程服務器。
6.3.1 客戶端程序需要做的工作
(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);
  
    ... ...
6.3.2 服務器端程序需要做的工作
(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是按照阻塞方式實現的,意味着對其調用直到滿足了所接收或發送的個數才返回。
6.3.3 例6-重定向到遠程服務器
   以下是服務器端代碼。
#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;
}
6.4嵌入診斷上下文NDC
   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");
    LOG4CPLUS_DEBUG(logger, "this is a NDC test")
   不必顯式地調用push/pop了,而且當出現異常時,能夠確保push與pop的調用是匹配的。
7 輸出過濾
7.1 利用日志級別進行輸出過濾
7.1.1 日志級別管理
   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。
7.1.2 利用日志級別進行輸出過濾
   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設置日志級別進行過濾。
7.1.3 例7-日志的優先級
#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
7.1.4 例8-運行時利用日志級別進行輸出過濾
#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
7.2 利用腳本配置進行輸出過濾
    由於log4cplus腳本配置中可以設置日志的級別、過濾器Filter,因此它也是進行輸出過濾的一種很好的選擇。腳本配置的使用具體參見第8節。
7.3 LogLog的輸出過濾
   Loglog可以使用setInternalDebugging()方法用來控制是否屏蔽輸出信息中的調試信息,當輸入參數為false則屏蔽,缺省設置為false。 另外方法setQuietMode()方法用來控制是否屏蔽所有輸出信息,當輸入參數為true則屏蔽,缺省設置為false。具體用法請參見4.2.5小節。
8 腳本配置
   除了通過程序實現對log環境的配置之外,log4cplus通過PropertyConfigurator類實現了基於腳本配置的功能。通過腳本可以完成對logger、appender和layout的配置,因此可以解決怎樣輸出,輸出到哪里的問題。
   下面將簡單介紹一下腳本的語法規則,包括基本配置語法和高級配置語法。
8.1 基本配置
   基本配置語法主要針對包括rootLogger和non-root logger。
8.1.1根Logger的配置
    語法:
   log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...
8.1.2非根Logger的配置
    語法:
   log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...
   說明:INHERITED表示繼承父Logger的日志級別。
8.2 高級配置
8.2.1 Appender配置
   語法:
   log4cplus.appender.appenderName=fully.qualified.name.of.appender.class
    舉例:
log4cplus.appender.append_1=log4cplus::ConsoleAppender
log4cplus.appender.append_2=log4cplus::FileAppender
log4cplus.appender.append_3=log4cplus::RollingFileAppender
log4cplus.appender.append_4=log4cplus::DailyRollingFileAppender
log4cplus.appender.append_4=log4cplus::SocketAppender
8.2.2 Filter配置
   Appender可以附加Filter組成的鏈表,如果Filter鏈中存在過濾器Filter, log4cplus在輸出日志之前將調用鏈表中Filter的過濾方法decide(),根據該方法的返回值決定是否過濾該輸出日志。
   語法:
   log4cplus.appender.appenderName.Filter.FilterNumber=fully.qualified.name.of.Filter.class
   log4cplus.appender.appenderName.Filter.FilterNumber.FilterCondition=value.of.FilterCondition
   舉例:
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。
LogLevelMatchFilter根據特定的日志級別進行過濾。
   過濾條件包括LogLevelToMatch和AcceptOnMatch(true|false), 只有當日志的LogLevel值與LogLevelToMatch相同,且AcceptOnMatch為true時才會匹配。
LogLevelRangeFilter根據根據日志級別的范圍進行過濾。
       過濾條件包括LogLevelMin、LogLevelMax和AcceptOnMatch,只有當日志的LogLevel在LogLevelMin、LogLevelMax之間同時AcceptOnMatch為true時才會匹配。
StringMatchFilter根據日志內容是否包含特定字符串進行過濾。
  過濾條件包括StringToMatch和AcceptOnMatch,只有當日志包含StringToMatch字符串 且AcceptOnMatch為true時會匹配。
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信息。
8.2.3 Layout配置
   可以選擇不設置、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
8.3.3 例9-腳本配置
   腳本方式使用起來非常簡單,只要首先加載配置即可(urconfig.properties是自行定義的配置文件):
   PropertyConfigurator::doConfigure("urconfig.properties");
  下面我們通過例子體會一下log4cplus強大的基於腳本過濾log信息的功能。以下是urconfig.properties示例腳本配置內容。
/*
 *    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

    以下是示例代碼。
/*
 *    main.cpp
 */
#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <log4cplus/helpers/stringhelper.h>

using namespace log4cplus;

static Logger logger = Logger::getInstance("log");

void printDebug()
{
    LOG4CPLUS_TRACE_METHOD(logger, "::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("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
8.3腳本配置的動態加載
   多線程版本的log4cplus提供了實用類ConfigureAndWatchThread,該類啟動線程對配置腳本進行監控,一旦發現配置腳本被更新則立刻重新加載配置。
   類ConfigureAndWatchThread的構造函數定義為:
   ConfigureAndWatchThread(const log4cplus::tstring& propertyFile,
                                unsigned int millis = 60 * 1000);
   第一個參數propertyFile為配置腳本的路徑名,第二個參數為監控時兩次更新檢查相隔的時間,單位為耗秒ms。
8.3.1 例10-使用線程監控腳本的更新
#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
9 定制Log4cplus
9.1 定制日志級別
    log4cplus支持日志級別的定制。如果需要定義自己的優先級,則可以按以下步驟進行定制。
(1) 定義新日志級別對應的常量整數和輸出宏。
/*
 * 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__); \
    }
(2)定義新日志級別對應的字符串、常量整數與字符串之間的轉換函數,定義自己的初始化器將轉換函數注冊到LogLevelManage。
/*
 * customloglevel.cxx
 */
#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_;

(3)使用新定義的日志級別
/*
 * main.cxx
 */
#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;
}
9.2 定制LogLog
   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 ") )
{
}
   可以把這些前綴換成自己需要的提示符號,然后重新編譯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM