1、Log4cpp簡介
Log4cpp是個基於LGPL的開源項目,移植自Java的日誌處理跟蹤項目log4j,並保持了API上的一致。其類似的支持庫還包括Java(log4j),C++(log4cpp、log4cplus),C(log4c),python(log4p)等。
Log4cpp有如下優點:
1. 提供了可擴展的多種日誌記錄方式;
2. 提供了NDC(嵌套診斷上下文),可用於多執行緒、多場景的跟蹤調試;
3. 提供了完整的日誌動態優先權控制,可隨時調整需要記錄的日誌優先權;
4. 可通過配置檔案完成所有配置並動態載入;
5. 性能優秀,記憶體占用小,經過編譯後的log4cpp.dll大小僅有160kb;
6. 代碼級的平台無關性,Log4cpp原始碼經過編譯後,適用於大多數主流的作業系統和開發工具;
7. 概念清晰,學習和使用方便,熟練程式設計師一天之內即可很好地套用log4cpp進行開發。
2、下載和安裝
2.1 下載
下載版本0.3.5rc3,這個版本目前是最穩定的,版本1.0在VC中表現不穩定。下載後的包名字為:log4cpp-0.3.5rc3.tar.gz(原始碼包)和log4cpp-docs-0.3.5rc3.tar.gz(文檔壓縮檔)。將它們解壓後放入D糟。
2.2 在VC6中編譯Log4cpp
進入D:\log4cpp-0.3.5rc3\msvc6目錄,打開VC6的工作區msvc6.dsw,將其中的工程都刪除,只保留log4cpp和log4cppDLL兩個工程。分別編譯它們的Debug和Release版本。
在VC6中編譯Log4cpp會報錯,其實只有一個錯誤,即不能在頭檔案中定義變數,同時給變數賦默認值。修改方法如下:將頭檔案Priority.hh中的這一行:
static const int MESSAGE_SIZE = 8;
改為:
static const int MESSAGE_SIZE;
並在Priority.cpp中的所有include語句後加上:const int log4cpp::Priority::MESSAGE_SIZE = 8;
編譯連結成功後會得到log4cppD.dll、log4cppD.lib(Debug版的dll和lib檔案)和log4cpp.dll、log4cpp.lib(Release版的dll和lib檔案)。新建目錄D:\log4cpp-0.3.5rc3\lib,將以上四個檔案拷貝到該目錄下。 在VC中添加設定lib和include路徑。
將D:\log4cpp-0.3.5rc3\lib加入系統的Path路徑中。
3、Log4cpp的HelloWorld
讓我們從一個簡單的例子開始,該例子將兩條日誌信息寫入字元串流,該流會在標準控制台cout上輸出,項目的名稱是HelloLog4Cpp:
#include <iostream>
#include "log4cpp/Category.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/Priority.hh"
using namespace std;
int main(int argc, char* argv[])
{
log4cpp::OstreamAppender* osAppender = new log4cpp::OstreamAppender("osAppender", &cout);
osAppender->setLayout(new log4cpp::BasicLayout());
log4cpp::Category& root = log4cpp::Category::getRoot();
root.addAppender(osAppender);
root.setpriority(log4cpp::Priority::DEBUG);
root.error("Hello log4cpp in a Error Message!");
root.warn("Hello log4cpp in a Warning Message!");
log4cpp::Category::shutdown();
return 0;
}
要順利編譯運行還有兩個地方需要設定,其一是引入的庫中加上log4cppD.lib(debug版dll庫的引入檔案),即在頭檔案處加上#pragma comment(lib, "log4cppD.lib");其二是將C/C++的Code Generation中的Use Runtime library設定為“Debug Multithreaded DLL”。 設定完成後編譯運行結果如下:
1248337987 ERROR : Hello log4cpp in a Error Message!
1248337987 WARN : Hello log4cpp in a Warning Message!
以上兩條日志格式很簡陋,要設定合乎心意的日誌格式,請參考後續的PatternLayout章節。
4、概念
Log4cpp中的概念繼承自log4j,最重要的是Category(種類)、Appender(附加目的地)和Layout(布局)三個概念,此外還有Priority(優先權)和NDC(嵌套的診斷上下文)等。
簡言之,Category負責向日誌中寫入信息,Appender負責指定日誌的目的地,Layout負責設定日誌的格式,Priority被用來指定Category的優先權和日誌的優先權, NDC則是一種用來區分不同場景中交替出現的日誌的手段。
Log4cpp記錄日誌的原理如下:每個Category都有一個優先權,該優先權可以由setPriority方法設定,或者從其父Category中繼承而來。每條日誌也有一個優先權,當Category記錄該條日誌時,若日誌優先權高於Category的優先權時,該日誌被記錄,否則被忽略。系統中默認的優先權等級如下:
typedef enum ...{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日誌都可以記錄下來,而其他則不能。
Category、Appender和Layout三者的關係如下:系統中可以有多個Category,它們都是繼承自同一個根,每個Category負責記錄自己的日誌;每個Category可以添加多個Appender,每個Appender指定了一個日誌的目的地,例如檔案、字元流或者Windows日誌,當Category記錄一條日誌時,該日誌被寫入所有附加到此Category的Appender;每個Append都包含一個Layout,該Layout定義了這個Appender上日誌的格式。
現在重溫前面的HelloWorld程式,可以發現其流程如下:
1. 創建一個Appender,並指定其包含的Layout;
2. 從系統中得到Category的根,將Appender添加到該Category中;
3. 設定Category的優先權;
4. 記錄日誌;
5. 關閉Category。
下面,我們按照Layout、Appender、Category、NDC的順序來依次介紹這些概念並給出例子。
5、Layout(布局)
首先回顧一下HelloWorld的日誌格式,它使用了最簡單的BasicLayout:
上面的日誌格式還可以,但顯然不是許多程式設計師心中理想的格式,許多人理想的格式應該是這樣的:
要獲得上面的格式,必須使用比BasicLayout複雜的PatternLayout,而且要花一個小時來熟悉一下PatternLayout的格式定義方式,如果你認為值得的話。 5.1 PatternLayout
在介紹PatternLayout以前,首先來看看log4cpp中所有的Layout子類(Layout本身是個虛類),一共三個:BasicLayout、PatternLayout和SimpleLayout,其中SimapleLayout並不建議使用,而BaiscLayout過於簡單,因此如果程式設計師不自己擴展Layout的話,就只能使用PatternLayout了,值得慶幸的是,PatternLayout還是比較好用的。
PatternLayout使用setConversionPattern函式來設定日誌的輸出格式。該函式的聲明如下:
其中參數類型為std::string,類似於C語言中的printf,使用格式化字元串來描述輸出格式,其具體含義如下:
因此,要得到上述的理想格式,可以將setConversionPattern的參數設定為“%d: %p %c %x: %m%n”,其具體含義是“時間: 優先權 Category NDC: 訊息 換行”。