Log4Cpp的使用(轉)


本文介紹如何使用Log4CPP。

Log4Cpp介紹

Log4Cpp的Api接口可以在http://log4cpp.sourceforge.net/api/index.html中查詢得到。

Log4Cpp中最主要的幾個概念是:Category、Appender、Layout以及Priority和NDC(嵌套的診斷上下文)。Category負責向日志中寫入信息,Appender負責制定日志的目的地,Layout負責設定日志中的格式,NDC則是用來區分不同場景中交替出現日志的手段。

Log4cpp記錄日志的原理如下:每個Category都有一個優先級,該優先級可以由setPriority方法設置,或者從其父Category中繼承而來。每條日志也有一個優先級,當Category記錄該條日志時,若日志優先級高於Category的優先級時,該日志被記錄,否則被忽略。系統中默認的優先級等級如下:

typedefenum {

EMERG  = 0,

FATAL  = 0,

ALERT  = 100,

CRIT   = 200,

ERROR  = 300,

WARN   = 400,

NOTICE =500,

INFO   = 600,

DEBUG  = 700,

NOTSET =800

}PriorityLevel;

  注意:取值越小,優先級越高。例如一個Category的優先級為101,則所有EMERG、FATAL、ALERT日志都可以記錄下來,而其他則不能。

 

CategoryAppenderLayout三者的關系如下系統中可以有多個Category,它們都是繼承自同一個根,每個Category負責記錄自己的日志;每個Category可以添加多個Appender,每個Appender指定了一個日志的目的地,例如文件、字符流或者Windows日志,當Category記錄一條日志時,該日志被寫入所有附加到此Category的Appender;每個Append都包含一個Layout,該Layout定義了這個Appender上日志的格式。

 

在詳細介紹Log4Cpp之前,我們先看一下Log4Cpp中的繼承關系:

(可在官網http://log4cpp.sourceforge.net/api/inherits.html查到)

布局(Layout)

布局類主要控制輸出日志消息的顯示樣式,從上圖中我們可以看到,布局類主要包括三個:

BasicLayout輸出格式為:    時間戳 優先級 category名字 :消息內容

如這樣1522761362 ERROR sub1      :  sub1 error

 

SimpleLayout輸出格式為:   優先級 :消息內容

                   如這樣 ERROR  :  Streamed sub1 error

 

PatternLayout,如果你想實現復雜的格式,就不想去自己繼承BasicLayout類自定義一個類,那使用這個布局也許就夠了。

PatternLayout使用setConversionPattern函數設置log的格式。函數原型如下:

void log4cpp::PatternLayout::setConversionPattern  (const std::string&  conversionPattern)  throw(ConfigureFailure) [virtual]

  其中參數類型為std::string,類似於C語言中的printf,使用格式化字符串來描述輸出格式,其具體含義如下:

%c category;

 

%d 日期;日期可以進一步的設置格式,用花括號包圍,例如%d{%H:%M:%S,%l} 或者 %d{%d %m %Y%H:%M:%S,%l}。如果不設置具體日期格式,則如下默認格式被使用“Wed Jan 02 02:03:55 1980”。日期的格式符號與ANSI C函數strftime中的一致。但增加了一個格式符號%l,表示毫秒,占三個十進制位。

補充關於strftime函數中的格式符號

%a        縮寫的星期幾名稱         Sun

%A        完整的星期幾名稱         Sunday

%b        縮寫的月份名稱     Mar

%B        完整的月份名稱     March

%c         日期和時間表示法         Sun Aug 19 02:56:02 2012

%d        一月中的第幾天(01-31)   19

%H        24 小時格式的小時(00-23)      14

%I         12 小時格式的小時(01-12)      05

%j 一年中的第幾天(001-366)        231

%m       十進制數表示的月份(01-12)   08

%M       分(00-59)   55

%p        AM 或 PM 名稱   PM

%S         秒(00-61)   02

%U        一年中的第幾周,以第一個星期日作為第一周的第一天(00-53)        33

%w       十進制數表示的星期幾,星期日表示為 0(0-6)   4

%W       一年中的第幾周,以第一個星期一作為第一周的第一天(00-53)        34

%x         日期表示法     08/19/12

%X        時間表示法     02:50:06

%y         年份,最后兩個數字(00-99)   01

%Y        年份         2012

%Z        時區的名稱或縮寫         CDT

%%       一個 % 符號 %

 

%m 消息;

 

%n 換行符,會根據平台的不同而不同,但對於用戶透明;

 

%p 優先級;

 

%r 自從layout被創建后的毫秒數;

 

%R 從1970年1月1日0時開始到目前為止的秒數;

 

%u 進程開始到目前為止的時鍾周期數;

 

%x NDC

 

具體使用如下:

    log4cpp::Appender *appender1 = new log4cpp::FileAppender("default", "patternLayout.log");

    log4cpp::PatternLayout * p = new log4cpp::PatternLayout();

    p->setConversionPattern("%d{%H:%M:%S,%l} : %c  %m%n");

    appender1->setLayout(p);

 

    log4cpp::Category& root = log4cpp::Category::getRoot();

    root.setPriority(log4cpp::Priority::WARN);

    root.addAppender(appender1);

 

    root.error("root error");

    root << log4cpp::Priority::ERROR << "Streamed root error";

 

log的輸出結果如下:

22:31:23,945 :   root error

22:31:23,973 :   Another streamed error

 

Appender

Log4cpp中所有可直接使用的Appender列表如下:

 

log4cpp::IdsaAppender                        // 發送到IDS或者

log4cpp::FileAppender                         // 輸出到文件

log4cpp::RollingFileAppender            // 輸出到回卷文件,即當文件到達某個大小后回卷

 

log4cpp::OstreamAppender               // 輸出到一個ostream類

log4cpp::RemoteSyslogAppender             // 輸出到遠程syslog服務器

log4cpp::StringQueueAppender       // 內存隊列

log4cpp::SyslogAppender                    // 本地syslog

log4cpp::Win32DebugAppender      // 發送到缺省系統調試器

log4cpp::NTEventLogAppender        // 發送到win事件日志

 

  其中SyslogAppender和RemoteSyslogAppender需要與Syslog配合使用,因此這里不介紹。順便提一句,Syslog是類Unix系統的一個核心服務,用來提供日志服務,在Windows系統中並沒有直接提供支持,當然可以用相關工具提供Windows系統中的syslog服務。IdsaAppender的功能是將日志寫入Idsa服務,這里也不介紹。因此主要介紹以下Appender:

 

log4cpp::FileAppender                      // 輸出到文件

 

log4cpp::RollingFileAppender         // 輸出到回卷文件,即當文件到達某個大小后回卷

 

log4cpp::OstreamAppender           // 輸出到一個ostream類

 

log4cpp::StringQueueAppender             // 內存隊列

 

log4cpp::Win32DebugAppender            // 發送到缺省系統調試器

 

OstreamAppender

  它可以將日志記入一個流,如果該流恰好是cout,則會在標准控制台上輸出。比printf優越的是,除了輸出消息外,還可以輕松的輸出時間、時鍾數、優先級等大量有用信息。OstreamAppender的使用非常簡單,創建一個OstreamAppender的具體方法如下:

log4cpp::OstreamAppender* osAppender = newlog4cpp::OstreamAppender("osAppender", &cout);

第一個參數指定OstreamAppender的名稱,第二個參數指定它關聯的流的指針。

StringQueueAppender

StringQueueAppender的功能是將日志記錄到一個字符串隊列中,該字符串隊列使用了STL中的兩個容器,即字符串容器std::string和隊列容器std::queue,具體如下:

std::queue<std::string> _queue;

   _queue變量是StringQueueAppender類中用於具體存儲日志的內存隊列。StringQueueAppender的使用方法與OstreamAppender類似,其創建函數只接收一個參數“名稱”,記錄完成后需要程序員自己從隊列中取出每條日志。

 

FileAppender和RollingFileAppender

  FileAppender和RollingFileAppender是log4cpp中最常用的兩個Appender,其功能是將日志寫入文件中。它們之間唯一的區別就是前者會一直在文件中記錄日志(直到操作系統承受不了為止),而后者會在文件長度到達指定值時循環記錄日志,文件長度不會超過指定值(默認的指定值是10M byte)。

  FileAppender的創建函數如下:

FileAppender(conststd::string& name, conststd::string& fileName, bool append = true, mode_tmode = 00644);

  一般僅使用前兩個參數,即“名稱”和“日志文件名”。第三個參數指示是否在日志文件后繼續記入日志,還是清空原日志文件再記錄。第四個參數說明文件的打開方式。

 

  RollingFileAppender的創建函數如下:

RollingFileAppender(const std::string&name,  const std::string&fileName, 

size_tmaxFileSize =10*1024*1024,  unsigned intmaxBackupIndex = 1,

boolappend = true,  mode_t mode =00644);

 

  它與FileAppender的創建函數很類似,但是多了兩個參數:maxFileSize指出了回滾文件的最大值;maxBackupIndex指出了回滾文件所用的備份文件的最大個數。所謂備份文件,是用來保存回滾文件中因為空間不足未能記錄的日志,備份文件的大小僅比回滾文件的最大值大1kb。所以如果maxBackupIndex取值為3,則回滾文件(假設其名稱是rollwxb.log,大小為100kb)會有三個備份文件,其名稱分別是rollwxb.log.1,rollwxb.log.2和rollwxb.log.3,大小為101kb。另外要注意:如果maxBackupIndex取值為0或者小於0,則回滾文件功能會失效,其表現如同FileAppender一樣,不會有大小的限制。這也許是一個bug。

Win32DebugAppender

  Win32DebugAppender是一個用於調試的Appender,其功能是向Windows的調試器中寫入日志,目前支持MSVC和Borland中的調試器。創建Win32DebugAppender僅需要一個參數“名稱”,其使用非常簡單,下面是例子代碼DebugAppenderExam:

 

#include <iostream>

#include <log4cpp/Category.hh>

#include <log4cpp/Appender.hh>

#include <log4cpp/Win32DebugAppender.hh>

#include <log4cpp/Priority.hh>

#include <log4cpp/PatternLayout.hh>

using namespace std;

 

int main(int argc, char* argv[]){

log4cpp::PatternLayout* pLayout1 = newlog4cpp::PatternLayout();

pLayout1->setConversionPattern("%d: %p %c%x: %m%n");

log4cpp::Appender* debugAppender = newlog4cpp::Win32DebugAppender("debugAppender");

debugAppender->setLayout(pLayout1);

 

log4cpp::Category& root =log4cpp::Category::getRoot().getInstance("RootName");

root.addAppender(debugAppender);

root.setPriority(log4cpp::Priority::DEBUG);

root.error("Root Error Message!");

root.warn("Root Warning Message!");

log4cpp::Category::shutdown();

return 0;

}

會在VS的debug調試信息中打印出來。

 

Category

  Log4cpp中有一個總是可用並實例化好的Category,即根Category。使用log4cpp::Category::getRoot()可以得到根Category。在大多數情況下,一個應用程序只需要一個日志種類(Category),但是有時也會用到多個Category,此時可以使用根Category的getInstance方法來得到子Category。不同的子Category用於不同的場合。

NDC

  NDC是nested DiagnosticContext的縮寫,意思是“嵌套的診斷上下文”。NDC是一種用來區分不同源代碼中交替出現的日志的手段。當一個服務端程序同時記錄好幾個並行客戶時,輸出的日志會混雜在一起難以區分。但如果不同上下文的日志入口擁有一個特定的標識,則可以解決這個問題。NDC就是在這種情況下發揮作用。注意NDC是以線程為基礎的,每個線程擁有一個NDC,每個NDC的操作僅對執行該操作的線程有效。

 

Log4cpp的自動內存管理

項目的多線程設置

   VC中必須將項目設置為Debug MultiThreaded DLL,總之這個設置必須與你使用的Log4cpp庫一致。如果你使用的是Release版本的log4cpp.dll,則應該設置為MultiThreaded DLL。

Log4cpp的內存對象管理

  也許讀者已經注意到,在前面的所有代碼中,log4cpp中所有動態分配的對象都沒有手動釋放。

  Log4cpp中new出來的Category、Appender和Layout都不需要手動釋放,因為Log4cpp使用了一個內部類來管理這些對象。此類的名稱是HierarchyMaintainer,它負責管理Category的繼承關系,在程序結束時,HierarchyMaintainer會依次釋放所有Category,而Category則會依次釋放擁有的有效Appender,Appender則會釋放所有附屬的Layout。如果程序員手動釋放這些對象,則會造成內存報錯。

 

  從下面的代碼可以看出這個特征:

appender->setLayout(newlog4cpp::BasicLayout());

  這個new出來的BasicLayout根本就沒有保存其指針,所以它只能被log4cpp的內存管理類HierarchyMaintainer釋放。

  了解到HierarchyMaintainer的內存管理方法后,程序員在使用log4cpp時應該遵循以下幾個使用原則:

不要手動釋放Category、Appender和Layout;

同一個Appender不要加入多個Category,否則它會被釋放多次從而導致程序崩潰;

同一個Layout不要附着到多個Appender上,否則也會被釋放多次導致程序崩潰;

log4cpp::Category::shutdown()

  在不使用log4cpp時可調用log4cpp::Category::shutdown(),其功能如同HierarchyMaintainer的內存清理。但如果不手動調用,在程序結束時HierarchyMaintainer會調用Category的析構函數來釋放所有Appender。

 

利用配置文件定制日志

如同log4j一樣,log4cpp也可以讀取配置文件來定制Category、Appender和Layout對象。其配置文件格式基本類似於log4j,一個簡單的配置文件log4cpp.ini例子如下:

 

#log4cpp配置文件

 

#定義Root category的屬性

log4cpp.rootCategory=DEBUG, RootLog

 

#定義RootLog屬性

log4cpp.appender.RootLog=ConsoleAppender

log4cpp.appender.RootLog.layout=PatternLayout

log4cpp.appender.RootLog.layout.ConversionPattern=%d [%p] -%m%n

 

#定義sample category的屬性

log4cpp.category.sample=DEBUG, sample

 

#定義sample屬性

log4cpp.appender.sample=FileAppender

log4cpp.appender.sample.fileName=sample.log

log4cpp.appender.sample.layout=PatternLayout

log4cpp.appender.sample.layout.ConversionPattern=%d [%p] -%m%n

 

#定義sample.soncategory的屬性

log4cpp.category.sample.son=DEBUG, son

 

#定義son的屬性

log4cpp.appender.son=FileAppender

log4cpp.appender.son.fileName=son.log

log4cpp.appender.son.layout=PatternLayout

log4cpp.appender.son.layout.ConversionPattern=%d[%p] - %m%n

 

#定義sample.daughtercategory的屬性

log4cpp.category.sample.daughter=DEBUG,daughter

 

#定義daughter屬性

log4cpp.appender.daughter=FileAppender

log4cpp.appender.daughter.fileName=daughter.log

log4cpp.appender.daughter.layout=PatternLayout

log4cpp.appender.daughter.layout.ConversionPattern=%d [%p]- %m%n

 

對應category 和 appender 的配置方式,可以發現

category 是"log4cpp.category." + "categoryname"

category 名字可以用"."分隔,以標識包含關系

appender 是"log4cpp.appender." + "appendername"

appender 名字 不能用 "." 分隔,即是說 appender 是沒有包含關系的

 

讀取配置文件要依賴PropertyConfigurator和SimpleConfigurator類。這里僅介紹PropertyConfigurator,其使用方法代碼ConfigFileExam所示(該代碼來自《便利的開發工具-log4cpp快速使用指南》一文):

 

#include<iostream>

#include<log4cpp/Category.hh>

#include<log4cpp/PropertyConfigurator.hh>

int main(int argc,char* argv[])

 

{

try

{

 

log4cpp::PropertyConfigurator::configure("./log4cpp.conf");

 

}

catch(log4cpp::ConfigureFailure& f)

{

std::cout<< "Configure Problem "<< f.what()<< std::endl;

 

return -1;

 

}

 

log4cpp::Category& root =log4cpp::Category::getRoot();

log4cpp::Category& sub1 =log4cpp::Category::getInstance(std::string("sub1"));

log4cpp::Category& sub3 =log4cpp::Category::getInstance(std::string("sub1.sub2"));

sub1.info("This is someinfo");

sub1.alert("Awarning");

// sub3 only have A2 appender.

 

sub3.debug("This debug messagewill fail to write");

sub3.alert("All hands abandonship");

 

sub3.critStream() <<"This will show up<< as "<< 1 <<" critical message"<<log4cpp::CategoryStream::ENDLINE;

sub3<<log4cpp::Priority::ERROR<<"And this will be anerror"  <<log4cpp::CategoryStream::ENDLINE;

sub3.log(log4cpp::Priority::WARN, "This will be a logged warning");

return0;

}

 

  該程序首先讀入了配置文件log4cpp.conf,從中得到了所有Category、Appender和Layout的優先級和相互附屬關系,然后輸出了一些日志,其運行結果如下:

 

1248875649 INFO sub1 : This is some info

 

1248875649 ALERT sub1 : A warning

 

The message All hands abandon ship at time 2009-07-2921:54:09,515

 

1248875649 ALERT sub1.sub2 : All hands abandonship

 

The message This will show up<< as 1 critical message at time2009-07-29 21:54:09,531

 

1248875649 CRIT sub1.sub2 : This will show up<< as 1 critical message

 

The message And this will be an error at time 2009-07-2921:54:09,531

 

1248875649 ERROR sub1.sub2 : And this will be anerror

 

轉載自https://blog.csdn.net/kingskyleader/article/details/7320826

 


免責聲明!

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



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