日志模塊
本文介紹的日志系統包括七個 C++ 類:CLog, CFuncLog, IStoreLog, CWinLog, CFileLog, CAutoCritic, CLogSimpleLock。CLog 是所有日志模塊中最主要的一個類,在大多數情況下,這個類在應用程序中最好是單實例的。但並不是必須的。重要性其次的一個類是 CFuncLog,這個類用於對函數調用的進入和退出進行記錄。當然開發人員也可以使用這個類對任何數據進行日志記錄。該類重載了 << 操作符,所以使用很方便。
下圖是本文介紹的日志系統使用截圖
日志模塊的 UML 設計
從下圖可以看到,日志模塊類可以分成兩個部分:
1. Storage 類 —— 存儲類
2. Logging 類 —— 日志類
圖一 UML 設計的類繼承圖
存儲類:
IStoreLog 類的聲明
/////////////////////////////////////////////////////////
// 此類是為一個抽象類,建立三個默認的函數,以便支持所有子類,
// 任何子類都必須支持緩沖和非緩沖存儲
/////////////////////////////////////////////////////////
class IStoreLog
{
public:
virtual ~IStoreLog( void ){}; // 虛擬析構函數
virtual int FlushData() = 0;
virtual int WriteString( const std::string &Message ) = 0;
virtual int SetBufferLimit( long lSize ) = 0;
};
存儲類能對數據緩沖進行清掃處理,缺省情況下,該類在自己的緩沖中存儲數據,並且只有調用 FlushData 函數才能將緩沖數據定向到磁盤或其它地方。一般情況下,存儲類的緩沖受系統資源的限制,當緩沖達到限制時它會自動進行清理。建立緩沖限制使用 SetBufferLimit 函數。默認情況下,存儲類實現必須要分配緩沖並只能用 SetBufferLimit 函數改變緩沖的大小。
存儲類使用 WriteString 函數來存儲串。所以它既可以存儲格式化串,也能存儲原始非格式化數據。
日志類
這一部分我們要討論兩個類:CLog 和 CFuncLog。CLog 在 clog.h 中聲明,實現文件是 clog.cpp,CFunLog 在 cfunlog.h 聲明,實現文件是 cfunlog.cpp。
日志類有一個很特別的函數,使用它來記錄日志很容易,你可以對其跟蹤輸出進行配置:如果你不需要記錄時間,只要用 SetLogTime 將 CLog 類的標志設為 FALSE 即可。此外你還可以調用 SetTimeFormat 來改變時間的輸出格式。缺省情況下,該類使用長時間格式。在頭文件的開始處有兩個很有用的時間格式定義。第一個是帶秒輸出的長格式,第二個是不帶秒輸出的短格式:
#define DEF_TIME_LONG_STR "%02u:%02u:%02u ms:%03u"
#define DEF_TIME_SHORT_STR "%02u:%02u:%02u"
警告:格式串最多四個,否則會有堆棧溢出錯誤
CLog 還能具備如下的屬性:
信息輸出格式屬性——SetMessageFormat 和 GetMessageFormat 函數;
AutoFlush 屬性——SetAutoFlush 和 GetAutoFlush 函數;如果是 true,則每次跟蹤信息都將緩沖數據存入磁盤,這對應用程序處於alpha測試和代碼中存在一般保護性錯誤時非常有用。第二種模式主要用於記錄應用程序的控制狀態以及應用程序的執行時間不是很重要時——這是日志系統的頂層性能模式。
在 CLog 類中有三個日志記錄函數:
LogRawString —— 跟蹤記錄原始串,不需要格式化存儲;
LogString —— 跟蹤記錄特定級別和格式的日志信息,有兩個函數名字都相同,其差別是輸出格式不同,兩種格式是:
std::string
char*
LogFormatString —— 格式化函數,實際上是對 printf 函數的封裝。
大多數情況下,開發人員如果需要超過三種類型以上的信息,可以實現 CLog 提供的一個虛擬函數 LevelText,在參數中提供一個表示日志級別 LEVEL 的數字。函數將用串形式返回一個類型名。默認情況下,一種類型規定的格式符號限制是12種,但可以用 SetMessageFormat 函數修改格式模板串。
如果要在應用程序中使用日志機制,請在工程中包含下列頭文件:
#include "clog.h"
#include "cfunclog.h"
#include "cwinlog.h"
// 在 GUI 窗口中顯示日志信息時包含
#include "cfilelog.h"
// 將日志信息寫入文件時包含
...
CFuncLog 類不是必須的,它只是簡化一些日志模塊的功能,如:進入函數和退出函數。
特殊格式和所有主要的日志操作都在 CLog 類中實現。所以你的選擇很靈活。
注意:CWinLog 類存儲跟蹤窗口中的信息,它不是多進程安全的。但多進程的日志可以使用 CFileLog 類。它將所有跟蹤信息存儲到文件。所有文件的操作通過操作系統進行同步。
如何使用日志模塊
1、創建 CLog 類實例:
CLog *m_pLog = new CLog( new CFileLog( "c:\\log.log" ), LOG_MAX_LEVEL, true );
CLog 構造函數的第一個參數必須是 IStoreLog 接口指針。日志模塊中有兩個 IStoreLog 實現:一個是 CWinLog,另一個是 CFileLog。前者創建 GUI 窗口,並在窗口中顯示日志信息,後者將日志信息存儲到文件中。由於輸出文件可被用於任何 OS 設備或命名管道等,它使用 CreateFile API 函數處理。
CLog 構造函數的第二個參數是日志信息的級別,可以根據需要來設置。所以,如果將它設置為 0,則輸出日志僅為 ERROR 信息。如果設置為 1,則日志輸出為 ERROR 和 WARNING 信息.....
第三個參數針對 CLog 實例的,說明這個實例是否為 IStoreLog 的父類。缺省值為 true。所以 CLog 類的析構函數要刪除 IStoreLog 類的實例。
如果你想要日志函數的進入和退出,可以向下面這樣:
CRepTestApp::CRepTestApp()
{
CFuncLog log( m_pLog, "CRepTestApp::CRepTestApp" );
...
}
上面的代碼將在進入函數和退出函數時記錄日志。這裡使用了自動變量的特性。進入函數時,構造函數記錄“enter.....”信息,退出函數時,析構函數在日志中記錄“leave......”,如果還想添加其它的日志信息,可以用如下代碼:
int something = 100;
log << something;
...
警告:操作符 << 以原始格式存儲日志信息。
使用 CFuncLog 類的 LogString 可以很容易添加需要的信息到日志中。
如果你想要將存儲到其它地方,那麼必須自己實現 IStoreLog 類,並用其實例作為某個構造函數的參數。CAutoCritic 和 CLogSimpleLock 類是對 Windows 臨界區 API 的封裝。這個類是在單獨的文件中實現的,並在名字空間 LOGGER 中使用。你可以通過其它的 IStoreLog 類靈活使用。
具體細節和應用例子請參考源代碼。
本文配套源碼