< .NET の属性を使用して、Enum と Struct をタイプライブラリに含める方法 | Word で、図形やワードアートの塗りつぶしの色が表示されない >

December 26, 2005

ログクラス

めもめも。
/**
 * @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

コメント

コメントする

※ コメントスパム対策のため、コメント本文はおはよう、こんにちわ、こんばんわのいずれかより始めるようにしてください。

name:
email:

※ 必要ですが、表示しません。

url:
情報を保存する ?