2011年5月18日 星期三

Log... Logging... Logger...

Dear All:
在經過了一段漫長的歲月(其實是一直在偷懶...XD),小弟我終於把Porman-gl的log系統確定下來了,在此做些記錄。

從定義開始:
甚麼是Log system?? 定義: Log system是輔助debug的系統或工具,當Programmer在撰寫程式的過程中,希望在某些可能發生錯誤或做測試的地方,ouput出一些有義意的文字訊息到檔案或者console視窗裡,來幫助執行期間或事後追蹤使用。

實做:
 通常在設計上,會對輸出的訊息做等級上的分類,一般來說,從最低到最高會分成:DEBUG、INFO、WARNING與ERROR這四種。而在DEBUG的部分又可以再以數字去分等級,例如:DEBUG1、DEBUG2、DEBUG3...etc,數字越高者代表越不重要的訊息。

為什麼要對訊息做分類?因為有時我們所log的訊息,在開發期間只是來除錯用,當程序發佈後,這些訊息就don't care了,但每當發佈版本就要Programmer把一堆開發用的訊息拿掉,是相當累人的一件事,況且你真的確定這些訊息之後就真的不需要了嗎?你敢就這樣刪除嗎?如果以後又要加回來怎麼辦呢?但不刪除,在發佈的版本中又log一堆有的沒的,耗效能不說,當出錯時,要在一堆沒用的訊息裡找到關鍵的問題所在,眼都花了,我想。

所以設計上會在log system裡加入一個等級變數,用這個等級變數來設定,哪些訊息需要被log、哪些可以被忽略不管,例如:當程序發佈時,我們通常會把等級變數設定為WARNING,這表示WARNING以上的訊息(如:INFO、DEBUG1、DEBUG2,DEBUG3...etc)不會被記錄,用這樣的方式確保當程序出錯時,不會有太多不必要的資訊混雜其中。

再來,輸出的目的地在哪?std::error、windows output還是logMsg.txt?都有可能,那就全部都做吧!其實有可以使用Observer Pattern來動態擴充,因為真的有太多可能性,與其一次想清楚,不如在class設計上預留擴充性,對吧!!

最後一個議題就是Multi-thread,一個應用程序或者lib裡通常只會有一個log system,一般來說我們也會使用Singleton pattern來限定這個系統只能有一個,但遇到需要在不同thread去log訊息時,該怎麼辦呢?沒錯就跟你想的一樣,用Mutex來解決,為每個log system裡,會寫入訊息的memeber function加入Mutex,來確保同一時間內只有一個thread會寫入訊息。

Example:
說了這麼多都沒用啦!來看code才是真的!!,以下為Porman-gl所實做的logger標頭檔:

enum LogLevel
{
    LOG_ERROR,
    LOG_WARNING,
    LOG_INFO,
    LOG_DEBUG,
    LOG_DEBUG1,
    LOG_DEBUG2,
    LOG_DEBUG3,
    LOG_DEBUG4
};

//-----------------------------
// class ILogReceiver:
//-----------------------------
class ILogReceiver
{
public:
    ILogReceiver( void ) : m_bEnable(true) {}
    virtual ~ILogReceiver( void ) {}
    virtual bool onLog( const std::string& sLevel, const std::string& sSender,
                                          const std::string& sMsg, int iLine ) = 0;

    bool isEnable( void ) { return m_bEnable; }
    void setEnable( bool bEnable ) { m_bEnable = bEnable; }

private:
    bool m_bEnable;
};
//-----------------------------
// class CLogger:
//-----------------------------
class CLogger
{
public:
    CLogger( void );
    virtual ~CLogger( void );

    void log( LogLevel level, const std::string& sSender,
                                        const std::string& sMsg, int iLine );
    void setLogLevel( LogLevel level );
    LogLevel getLogLevel( void );
    void registerLogReceiver( ILogReceiver* pReceiver, const std::string& sName );
    bool removeLogReceiver( const std::string& sName );
    bool isLogReceiverRegistered( const std::string& sName );

private:
    LogLevel m_enumCurLogLevel;
    std::map m_mapLogReceiver;
};
以上的說明希望對"未來的我" or "正在閱讀的你"有幫助,謝謝!!

沒有留言:

張貼留言

LinkWithin

Related Posts Plugin for WordPress, Blogger...