ログクラス
めもめも。
/** * @file log.h * @brief ログクラスの宣言ヘッダ * @version 1.0 * @author ba * @date 2005/11 */ #ifndef __LOG__H #define __LOG__H /** * @enum EnumLogLevel * ログレベルの値 */ enum EnumLogLevel { nLogLevelInfo = _T ('I'), //!< INFO 情報 (常に出力する) nLogLevelWarning = _T ('W'), //!< WARNNING 警告 (INIファイルにW指定時のみ) nLogLevelError = _T ('E'), //!< ERROR エラー (常に出力する) nLogLevelTrace = _T ('T'), //!< TRACE デバッグ/トレース (INIファイルにT指定時のみ) }; /** * @class CLog * ログクラス */ class CLog { public: CLog (void); virtual ~CLog (void); static void Write (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, ...); static void Dump (int nLevel, LPCTSTR lpszSource, int nLine, int nBytesBuffer, LPBYTE lpbBuffer, LPCTSTR lpszLabel = NULL); static void W32Error (int nLevel, LPCTSTR lpszSource, int nLine, DWORD dwLastError, LPCTSTR lpszLabel = NULL); protected: class CLogImpl; }; #endif //__LOG__H
/** * @file log.cpp * @brief ログクラスの実装 * @version 1.0 * @author ba * @date 2005/11 */ #include <windows.h> #include <tchar.h> #include <stdio.h> #include <stdarg.h> #include <vector> #include <algorithm> #include "Log.h" #define SECTION_LOG _T ("LogDir") #define KEY_LOG_DIR _T ("Dir") #define KEY_LOG_LEVEL _T ("Level") #define KEY_LOG_BACKUPNUM _T ("BackupNum") /** * class CLogImpl * ログ機能の実装. */ class CLog::CLogImpl : public CLog { public: static CLogImpl& Get (void); ~CLogImpl (void); void Write (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, va_list args); void Dump (int nLevel, LPCTSTR lpszSource, int nLine, int nBytesBuffer, LPBYTE lpbBuffer, LPCTSTR lpszLabel = NULL); protected: TCHAR m_szLogDir [MAX_PATH]; //!< ログディレクトリパス BOOL m_bWarn; //!< Warnning 出力フラグ BOOL m_bTrace; //!< Trace 出力フラグ int m_nBackupNum; //!< ログのバックアップ数 CRITICAL_SECTION m_csLogWrite; //!< ログアクセス用クリティカルセクション static CLogImpl s_Singleton; //!< 唯一のインスタンス CLogImpl (void); BOOL Initialize (void); HANDLE CreateLogFile (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, va_list args); HANDLE CreateLogFile (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, ...); BOOL IsIgnoreLevel (int nLevel); void PurgeOldLog(void); }; //******************************************************* // //******************************************************* // // CLog のインプリメンツ // /** * コンストラクタ. * CLog の初期化を行います */ CLog::CLog(void) { } /** * デストラクタ. * CLog の終業処理を行います */ CLog::~CLog(void) { } /** * ログ出力処理. * ログを出力します * @param[in] nLevel ログレベル * @param[in] lpszSource ソースファイル * @param[in] nLine 行番号 * @param[in] lpszFormat 書式文字列 * @param[in] ... 追加パラメータ */ void CLog::Write (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, ...) { va_list args; va_start (args, lpszFormat); CLogImpl::Get ().Write (nLevel, lpszSource, nLine, lpszFormat, args); va_end (args); } /** * ダンプログ出力処理. * ダンプログを出力します * @param[in] nLevel ログレベル * @param[in] lpszSource ソースファイル * @param[in] nLine 行番号 * @param[in] nBytesBuffer バッファのバイト数 * @param[in] lpbBuffer バッファのポインタ * @param[in] lpszLabel 任意のラベル */ void CLog::Dump (int nLevel, LPCTSTR lpszSource, int nLine, int nBytesBuffer, LPBYTE lpbBuffer, LPCTSTR lpszLabel) { CLogImpl::Get ().Dump (nLevel, lpszSource, nLine, nBytesBuffer, lpbBuffer, lpszLabel); } /** * WIN32 エラーログ出力処理. * WIN32のエラーコードを元にエラーログを出力します * @param[in] nLevel ログレベル * @param[in] lpszSource ソースファイル * @param[in] nLine 行番号 * @param[in] dwLastError WIN32エラーコード * @param[in] lpszLabel 任意のラベル */ void CLog::W32Error (int nLevel, LPCTSTR lpszSource, int nLine, DWORD dwLastError, LPCTSTR lpszLabel) { LPVOID lpvBuffer = NULL; // システムのエラーメッセージを取得 DWORD dwLength = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwLastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpvBuffer, 0, NULL); if (dwLength) { LPTSTR lpszBuffer = new TCHAR [dwLength + 1]; if (lpszBuffer) { LPTSTR ptr1 = (LPTSTR) lpvBuffer; LPTSTR ptr2 = lpszBuffer; while (*ptr1) { switch (*ptr1) { case _T ('\n'): case _T ('\r'): break; default: *ptr2 ++ = *ptr1; break; } ptr1 ++; } *ptr2 = _T ('\0'); if (lpszLabel) { Write (nLevel, lpszSource, nLine, _T ("[%-18.18s] <%d> %s"), lpszLabel, dwLastError, lpszBuffer); } else { Write (nLevel, lpszSource, nLine, _T ("<%d> %s"), dwLastError, lpszBuffer); } delete [] lpszBuffer; } LocalFree (lpvBuffer); } } //******************************************************* // //******************************************************* // // CLogImpl のインプリメンツ // CLog::CLogImpl CLog::CLogImpl::s_Singleton; //!< 唯一のインスタンス /** * コンストラクタ. * CLogImpl の初期化を行います */ CLog::CLogImpl::CLogImpl (void) : m_bWarn (FALSE), m_bTrace (FALSE) { // メンバ初期化 ZeroMemory (m_szLogDir, sizeof m_szLogDir); // INIファイル読み込み Initialize (); // クリティカルセクション初期化 InitializeCriticalSection (&m_csLogWrite); } /** * デストラクタ. * CLogImpl の終業処理を行います */ CLog::CLogImpl::~CLogImpl (void) { // クリティカルセクション解放 DeleteCriticalSection (&m_csLogWrite); } /** * シングルトンアクセス. * CLogImpl の唯一のインスタンスを取得します * @return CLogImpl のシングルトン */ CLog::CLogImpl& CLog::CLogImpl::Get (void) { return s_Singleton; } /** * ログ出力処理. * ログを出力します * @param[in] nLevel ログレベル * @param[in] lpszSource ソースファイル * @param[in] nLine 行番号 * @param[in] lpszFormat 書式文字列 * @param[in] args 追加パラメータ */ void CLog::CLogImpl::Write (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, va_list args) { // ログレベルを判断 if (IsIgnoreLevel (nLevel)) { return; } // クリティカルセクション所有 EnterCriticalSection (&m_csLogWrite); // ファイルを作成して書き込む HANDLE hFile = CreateLogFile (nLevel, lpszSource, nLine, lpszFormat, args); if (INVALID_HANDLE_VALUE != hFile) { // クローズする CloseHandle (hFile); } // クリティカルセクションを開放 LeaveCriticalSection (&m_csLogWrite); } /** * ダンプログ出力処理. * ダンプログを出力します * @param[in] nLevel ログレベル * @param[in] lpszSource ソースファイル * @param[in] nLine 行番号 * @param[in] nBytesBuffer バッファのバイト数 * @param[in] lpbBuffer バッファのポインタ * @param[in] lpszLabel 任意のラベル */ void CLog::CLogImpl::Dump (int nLevel, LPCTSTR lpszSource, int nLine, int nBytesBuffer, LPBYTE lpbBuffer, LPCTSTR lpszLabel) { // ログレベル判断 if (IsIgnoreLevel (nLevel)) { return; } // クリティカルセクション所有 EnterCriticalSection (&m_csLogWrite); // ファイルを作成してヘッダ部分を書き込む HANDLE hFile; if (lpszLabel) { hFile = CreateLogFile (nLevel, lpszSource, nLine, _T ("<< %s >> %d bytes"), lpszLabel, nBytesBuffer); } else { hFile = CreateLogFile (nLevel, lpszSource, nLine, _T ("%d bytes"), nBytesBuffer); } // 成功したら、ダンプ部分を書き込む if (INVALID_HANDLE_VALUE != hFile) { DWORD dwBytesWritten; const TCHAR cszHeader [] = _T ("-------- +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF\r\n"); BOOL bRet = WriteFile (hFile, (LPCVOID) cszHeader, lstrlen (cszHeader), &dwBytesWritten, NULL); if (bRet) { int nBytesCount = 0; LPBYTE lpbPtr = lpbBuffer; while (bRet && (nBytesBuffer - nBytesCount)) { TCHAR szLine [10] = _T (""); TCHAR szDump [50] = _T (""); TCHAR szAscii [18] = _T (""); LPTSTR lpszDump = szDump; LPTSTR lpszAscii = szAscii; // 行番号 wsprintf (szLine, _T ("%08X"), nBytesCount / 0x10); do { // ヘキサダンプ部分 wsprintf (lpszDump, _T ("%02X "), *lpbPtr); lpszDump += 3; // ASCII部分 if (0x20 > *lpbPtr) { *lpszAscii = _T ('.'); } else { *lpszAscii = (TCHAR) *lpbPtr; } lpszAscii ++; // ポインタをインクリメント lpbPtr ++; nBytesCount ++; } while ((nBytesCount % 0x10) && (nBytesBuffer - nBytesCount)); // 1行分か、最後まで // ダンプ結果を1行にまとめて書き込む TCHAR szLog [sizeof cszHeader]; ZeroMemory (szLog, sizeof szLog); DWORD dwBytesWrite = wsprintf (szLog, _T ("%s %-48.48s %-16.16s\r\n"), szLine, szDump, szAscii); bRet = WriteFile (hFile, (LPCVOID) szLog, dwBytesWrite, &dwBytesWritten, NULL); } // 最後に空行を追加する if (bRet) { WriteFile (hFile, (LPCVOID) _T ("\r\n"), 2, &dwBytesWritten, NULL); } } CloseHandle (hFile); } // クリティカルセクションを解放する LeaveCriticalSection (&m_csLogWrite); } /** * Iniファイルロード. * @retval TRUE 正常終了 * @retval FALSE 異常終了 */ BOOL CLog::CLogImpl::Initialize (void) { BOOL bRet = FALSE; int nRet; TCHAR szInifile [MAX_PATH]; TCHAR szWork [4096]; nRet = GetModuleFileName (NULL, szInifile, MAX_PATH); if (nRet) { LPTSTR lpszExt = _tcsrchr (szInifile, _T ('.')); if (lpszExt) { lstrcpy (lpszExt, _T (".ini")); } nRet = GetPrivateProfileString (SECTION_LOG, KEY_LOG_DIR, _T (".\\log\\"), m_szLogDir, MAX_PATH, szInifile); nRet = GetPrivateProfileString (SECTION_LOG, KEY_LOG_LEVEL, _T ("IE"), szWork, 4096, szInifile); LPTSTR pWork = szWork; while (*pWork) { switch (*pWork) { case 'W': m_bWarn = TRUE; break; case 'T': m_bTrace = TRUE; break; default: break; } pWork ++; } nRet = GetPrivateProfileInt (SECTION_LOG, KEY_LOG_BACKUPNUM, 1, szInifile); m_nBackupNum = nRet; return TRUE; } return FALSE; } /** * 共通部分出力処理. * ログファイルをオープンし、共通部分を書き込み、ファイルハンドルを返します * @param[in] nLevel ログレベル * @param[in] lpszSource ソースファイル * @param[in] nLine 行番号 * @param[in] lpszFormat 書式文字列 * @param[in] ... 追加パラメータ * @return ファイルハンドル * @retval INVALID_HANDLE_VALUE 異常終了 */ HANDLE CLog::CLogImpl::CreateLogFile (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, ...) { va_list args; va_start (args, lpszFormat); HANDLE hRet = CreateLogFile (nLevel, lpszSource, nLine, lpszFormat, args); va_end (args); return hRet; } /** * 共通部分出力処理. * ログファイルをオープンし、共通部分を書き込み、ファイルハンドルを返します * @param[in] nLevel ログレベル * @param[in] lpszSource ソースファイル * @param[in] nLine 行番号 * @param[in] lpszFormat 書式文字列 * @param[in] args 追加パラメータ * @return ファイルハンドル * @retval INVALID_HANDLE_VALUE 異常終了 */ HANDLE CLog::CLogImpl::CreateLogFile (int nLevel, LPCTSTR lpszSource, int nLine, LPCTSTR lpszFormat, va_list args) { // ログのパージを行う PurgeOldLog (); // ファイル名 TCHAR szLogPath [MAX_PATH]; SYSTEMTIME LocalTime; ZeroMemory (&LocalTime, sizeof (SYSTEMTIME)); GetLocalTime (&LocalTime); wsprintf (szLogPath, _T ("%s%04d%02d%02d.log"), m_szLogDir , LocalTime.wYear , LocalTime.wMonth , LocalTime.wDay); // ファイルをオープンする HANDLE hFile = CreateFile (szLogPath, GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != hFile) { // ポインタをファイルの末尾に移動 SetFilePointer (hFile, 0, NULL, FILE_END); int cnAllocUnit = 2048; int nAlloc = 0; int nRet = -1; LPTSTR lpszBuffer = NULL; // ログを書式化 while (true) { delete [] lpszBuffer; nAlloc += cnAllocUnit; lpszBuffer = new TCHAR [nAlloc]; if (!lpszBuffer) { break; } ZeroMemory (lpszBuffer, sizeof (TCHAR) * nAlloc); nRet = _vsntprintf (lpszBuffer, nAlloc, lpszFormat, args); if (-1 != nRet) { // ソースファイルパスよりファイル名を抽出 LPTSTR lpszSourceFile = _tcsrchr (lpszSource, _T ('\\')); if (lpszSourceFile) { lpszSourceFile ++; } else { lpszSourceFile = const_cast <LPTSTR> (lpszSource); } // ログの定型部分 TCHAR szHeader [128]; ZeroMemory (szHeader, sizeof szHeader); DWORD dwBytesWrite = wsprintf (szHeader, _T ( "%c,%04d/%02d/%02d %02d:%02d:%02d.%03d,TID:%08X" // ",\"%s\",L:%d" ",") , nLevel , LocalTime.wYear , LocalTime.wMonth , LocalTime.wDay , LocalTime.wHour , LocalTime.wMinute , LocalTime.wSecond , LocalTime.wMilliseconds , GetCurrentThreadId () // , lpszSourceFile, nLine ); DWORD dwBytesWritten; BOOL bRet = WriteFile (hFile, (LPCVOID) szHeader, dwBytesWrite, &dwBytesWritten, NULL); if (bRet) { dwBytesWrite = nRet; bRet = WriteFile (hFile, (LPCVOID) lpszBuffer, dwBytesWrite, &dwBytesWritten, NULL); if (bRet) { bRet = WriteFile (hFile, (LPCVOID) _T ("\r\n"), 2, &dwBytesWritten, NULL); } } delete [] lpszBuffer; if (!bRet) { break; } return hFile; } } CloseHandle (hFile); hFile = INVALID_HANDLE_VALUE; } return hFile; } /** * ログレベル判断処理. * ログレベルが現在出力無効に設定されているかを判断します * @param[in] nLevel ログレベル * @retval TRUE 出力無効 * @retval FALSE 出力有効 */ BOOL CLog::CLogImpl::IsIgnoreLevel (int nLevel) { switch (nLevel) { case nLogLevelInfo: case nLogLevelError: break; // 常に case nLogLevelWarning: if (!m_bWarn) return TRUE; break; case nLogLevelTrace: if (!m_bTrace) return TRUE; break; default: return TRUE; } return FALSE; } /** * ログファイルパージ処理. * 最新のログより設定された個数のみ残し、古いログを削除します */ void CLog::CLogImpl::PurgeOldLog (void) { if (0 == m_nBackupNum) {// 0は "パージなし" return; } HANDLE hFind = NULL; WIN32_FIND_DATA FindData; TCHAR szFindFile [MAX_PATH]; ZeroMemory (&FindData, sizeof (WIN32_FIND_DATA)); ZeroMemory (szFindFile, sizeof szFindFile); wsprintf (szFindFile, _T ("%s*.log"), m_szLogDir); // GetFullPathName (szFindFile, MAX_PATH, szFindFile, NULL); // ログファイルを列挙 hFind = FindFirstFile (szFindFile, &FindData); if (INVALID_HANDLE_VALUE != hFind) {// 正常 std::vector <int> files; // ファイルの日付を格納する do { int nDayInt = atoi (FindData.cFileName); files.push_back (nDayInt); } while (FindNextFile (hFind, &FindData)); // 昇順にソート std::sort (files.begin (), files.end ()); // 後方よりループ std::vector <int>::reverse_iterator& itr = files.rbegin (); for (int nCount = 0; files.rend () != itr; itr ++, nCount ++) { if (nCount > m_nBackupNum) {// 指定された個数を超えたところより削除 TCHAR szDeleteFile [MAX_PATH]; ZeroMemory (szDeleteFile, sizeof szDeleteFile); wsprintf (szDeleteFile, _T ("%s%08d.log"), m_szLogDir, *itr); DeleteFile (szDeleteFile); } } FindClose (hFind); } }
トラックバック
- このエントリーにトラックバック:
- http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/1220
コメント