在經過了一段漫長的歲月(其實是一直在偷懶...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 "正在閱讀的你"有幫助,謝謝!!
沒有留言:
張貼留言