程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> ASP編程 >> ASP技巧 >> Windows Mobile下使用Native C++開發日志類

Windows Mobile下使用Native C++開發日志類

編輯:ASP技巧

背景
這段業余時間一直都在開發iToday。在iToday中加入日志管理。關於iToday,可以參考那些一些文章。

開源(Open Source)那些事兒 (一)

開源那些事兒 (二) - iToday開源項目計劃

開源那些事兒(三)-iToday的總體設計

開源那些事兒(四)-如何使用CodePlex進行項目管理

 

簡介
日志管理是程序不可以缺少的一個重要組成部分,對於長期運行的後台程序尤為重要,盡管經過了大量的測試,但是在實際運行環境下,程序未免有出錯的時候。有時候由於第三方原因導致的,例如電信網絡質量下載,掉包等等。在一些看似莫名其妙的問題下,日志文件很多時候就成了救命繩。bug free是我們一直追求的目標,但是我永遠不能保證bug free,每次我在面試中說這句話,做銷售出生的人會翻白眼,做技術的人會會心一笑。我能保證的是如何盡快的troubleshooting,提高質量,日志文件在這過程中又是最重要的手段之一。下面文章講述使用Native C++對Windows Embedded CE和Windows Mobile日志文件類的封裝。

 

代碼
先上代碼,下面分析。需要iToday全部代碼也可以到codeplex上去下載。

類定義文件

typedef enum tagLOG_LEVEL{    LOG_TRACE,    LOG_INFO,    LOG_WARNING,    LOG_ERROR,    LOG_FATAL,    LOG_NONE = 10,}LOG_LEVEL;class Logger{public:    static Logger& Instance();        static void SetLogFilePath( const std::string& strFilePath);    static void SetLogLevel( const LOG_LEVEL enLogLevel);    static void Initialise();    static void Dispose();    //void Log( LOG_TRACE const TCHAR *format, ...  );    //void LogInfo( const TCHAR *format, ...  );    //void LogWarning( const TCHAR *format, ...  );    //void LogError( const TCHAR *format, ...  );    //void LogFatal( const TCHAR *format, ...  );        void Log( LOG_LEVEL logLevel ,const TCHAR *format, ...  );    PRivate:    /* more (non-static) functions here */    Logger(); // ctor hidden    Logger(Logger const&); // copy ctor hidden    Logger& Operator=(Logger const&); // assign op. hidden    ~Logger(); // dtor hidden    static FILE*            m_hLogFile;    static std::string        m_strFilePath;    static LOG_LEVEL        m_enLogLevel;};
類實現文件

FILE* Logger::m_hLogFile = NULL;LOG_LEVEL Logger::m_enLogLevel = LOG_TRACE;std::string Logger::m_strFilePath = "\\Storage Card\\DebugInfo.log";TCHAR * LogLevelStr[]={    TEXT("TRACE"),    TEXT("INFO"),    TEXT("WARN"),    TEXT("ERROR"),    TEXT("FATAL"),};Logger& Logger::Instance() {  static Logger oLogger;  return oLogger;}void Logger::SetLogFilePath( const std::string& strFilePath){    m_strFilePath = strFilePath;    Dispose();    Initialise();}void Logger::SetLogLevel( const LOG_LEVEL enLogLevel){    m_enLogLevel = enLogLevel;}Logger::Logger(){    Initialise();}//never useLogger::~Logger(){    Dispose();}void Logger::Initialise(){    if( m_strFilePath.length() > 0 )    {        m_hLogFile = fopen(m_strFilePath.c_str(), "a+");        }}void Logger::Dispose(){    if( NULL != m_hLogFile )    {        fflush( m_hLogFile );        fclose( m_hLogFile );        m_hLogFile = NULL;    }}void Logger::Log( LOG_LEVEL enLogLevel ,const TCHAR *format, ... ){    if( m_enLogLevel > enLogLevel)    {        return;    }#ifndef DEBUG    if ( NULL == m_hLogFile )    {        return;        }#endif    TCHAR szBuffer[1024];    va_list args;    va_start(args, format);    vswprintf(szBuffer, format, args);    va_end(args);#ifdef DEBUG    wprintf(_T("%S THR:%8.8x %s\t%s\n"), GetCurrentTime(), GetCurrentThread(), LogLevelStr[enLogLevel], szBuffer);#else    //combine time stamp, thread number and log level together.    if( 0 > fwprintf(m_hLogFile, _T("%S THR:%8.8x %s\t%s\n"), GetCurrentTime(), GetCurrentThreadId(), LogLevelStr[enLogLevel], szBuffer) )    {        Dispose();    }    else    {        fflush(m_hLogFile);    }#endif    }

Singleton模式
這個Logger類使用Singleton模式來實現,不知道什麼時候開始博客園已經不再流行設計模式了,一方面說明設計模式不再是陽春白雪,已經深入人間。另一方面又興起了反模式熱潮。在反模式的風潮中,Singleton是給人批評最多的模式,Singleton有點像變相的全局變量,破壞了封裝,混亂了各個類的依賴關系。

我還是那句話,模式本身沒有錯,看用的人是否把特定的模式用在特定的場景下。Singleton我還是會用到,如果某個資源類有且只有一份,我就使用Singleton。沒有必要產生多個對象,而且多個對象訪問獨占資源會有同步問題。在Logger類,我還是使用Singleton,因為我只寫一個文件。

Singleton的具體實現一般關心三個問題: 1. 有且只有一個對象實例化。 2.多線程的控制。其實第二個問題也是為了保證第一個問題。3. 按需實例化。

private:    /* more (non-static) functions here */    Logger(); // ctor hidden    Logger(Logger const&); // copy ctor hidden    Logger& Operator=(Logger const&); // assign op. hidden    ~Logger(); // dtor hidden上面的代碼用於保證只有一個對象的實例化,很多做C#的開發人員會忽略上面的代碼,因為C#沒有深拷貝的概念,也沒有運算符重載的概念。

 

Logger& Logger::Instance() {  static Logger oLogger;  return oLogger;}上面的代碼保證線程安全以及按需實例化。我覺得這個實現模式很好,同時滿足三個願望。

 

日志分級管理
打印日志的時候,分級管理很重要,不同時期需要顯示不同級別的日志,開發時期,可能需要Trace級別的日志,到了運行時可能只需要Error以上級別的日志了,日志分級管理能均衡時間與空間的合理利用。

通過級別管理,打印級別高於需要顯示級別的日志。

if( m_enLogLevel > enLogLevel){    return;}

在打印過程中,顯示級別,我在找問題的時候都是從高級往低級找。

if( 0 > fwprintf(m_hLogFile, _T("%S THR:%8.8x %s\t%s\n"), GetCurrentTime(), GetCurrentThreadId(), LogLevelStr[enLogLevel], szBuffer) ){    Dispose();}由於這是在Windows Embedded CE和Windows Mobile平台下的實現,所以都是有Unicode的API。下面是打印的日志,包含了級別。

2010-02-24T09:17:38 THR:de428d7e TRACE    FILE=[.\iToday.cpp], LINE=[44]2010-02-24T09:17:39 THR:de428d7e INFO    FILE=[.\iToday.cpp], LINE=[47]2010-02-24T09:17:39 THR:de428d7e WARN    FILE=[.\iToday.cpp], LINE=[50]2010-02-24T09:17:40 THR:de428d7e ERROR    FILE=[.\iToday.cpp], LINE=[53]2010-02-24T09:17:40 THR:de428d7e FATAL    FILE=[.\iToday.cpp], LINE=[56]2010-02-24T09:17:41 THR:de428d7e WARN    FILE=[.\iToday.cpp], LINE=[67]2010-02-24T09:17:42 THR:de428d7e ERROR    FILE=[.\iToday.cpp], LINE=[70]2010-02-24T09:17:42 THR:de428d7e FATAL    FILE=[.\iToday.cpp], LINE=[73]


時間與線程號
在多線程環境下,打印時間和線程十分重要,這樣能查線程同步問題。時間和線程號見上面的日志。

 

使用Logger
void LoggerTest(){    Logger::Instance().Log(LOG_TRACE, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);        Logger::Instance().Log(LOG_INFO, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_WARNING, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_ERROR, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_FATAL, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::SetLogLevel(LOG_WARNING);    Logger::Instance().Log(LOG_TRACE, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);        Logger::Instance().Log(LOG_INFO, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_WARNING, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_ERROR, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_FATAL, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::SetLogLevel(LOG_INFO);    Logger::SetLogFilePath("\\Storage Card\\DebugInfo2.log");    Logger::Instance().Log(LOG_TRACE, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);        Logger::Instance().Log(LOG_INFO, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_WARNING, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_ERROR, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);    Logger::Instance().Log(LOG_FATAL, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);    Sleep(500);}使用Logger類很簡單,直接調用Log()函數就可以了,可以參考printf的模式來使用,也就是C#的String.Format()的模式。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved