본문 바로가기

Programming/Windows Socket Programming

Write log to file - Code

WSLog.h

#pragma once
#define MAX_MSG_LEN 4096
#define MAX_VA_LEN 3072
#define MAX_DIRPATH_LEN 1024
#define MAX_ABSOLUTEPATH_LEN    2048
#define MAX_FILENAME    1024
#define UM_WRITELOG    WM_USER+6654
#define UM_TERMINATE_THREAD WM_USER+2

#define LOG_DEBUG    1
#define LOG_INFO    2
#define LOG_WARNING    3
#define LOG_ERROR    4

#define LOGOUT(int, arg,...) DbgMsg(int, _T("[%s] ") arg, _T(__FUNCTION__), ##__VA_ARGS__)

BOOL InitLog();
void DbgMsg(int nLogLevel, const TCHAR* pFmt, ...);
BOOL SetLogPath(TCHAR* pLogPath, TCHAR* pModuleName);
UINT WriteLog_Thread(LPVOID pParam);
UINT DeleteLog_Thread(LPVOID pParam);

WSLog.cpp

#include "stdafx.h"
#include "WSLog.h"

BOOL g_bInitLog = FALSE;
DWORD g_WriteLogThreadID = -1;
DWORD g_DeleteLogThreadID = -1;
TCHAR g_szAbsoluteLogPath[MAX_ABSOLUTEPATH_LEN] = { 0 };
TCHAR g_szLogPath[MAX_DIRPATH_LEN] = { 0 };
TCHAR g_szModuleName[MAX_FILENAME] = { 0 };
HANDLE g_hWSLogEvent = NULL;

/*
Log는 어디서나 찍을 수 있어야 한다. 즉 static속성을 가져야 한다.
이 경우 클래스로 만드는 것은 적합하지 않다. 배보다 배꼽이 더 큰 형상이다.
InitLog()는 사용자가 직접 호출할 필요 없다. 사용자는 LOGOUT()만 사용하면 된다.
*/
BOOL InitLog()
{
    if (g_bInitLog) {
        return FALSE;
    }

    if (g_WriteLogThreadID == -1) {
        HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WriteLog_Thread,
            NULL, 0, &g_WriteLogThreadID);
        if(hThread == INVALID_HANDLE_VALUE) {
            return FALSE;
        }
        CloseHandle(hThread);
    }
    g_hWSLogEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    if (g_DeleteLogThreadID == -1) {
        HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DeleteLog_Thread,
            NULL, 0, &g_DeleteLogThreadID);
        if (hThread == INVALID_HANDLE_VALUE) {
            return FALSE;
        }
        CloseHandle(hThread);
    }

    if (g_szLogPath == NULL || _tcscmp(g_szLogPath,_T("")) == 0 
        || g_szModuleName == NULL || _tcscmp(g_szModuleName, _T("")) == 0) {
        if (!SetLogPath(NULL, NULL)) {
            return FALSE;
        }
    }

    g_bInitLog = TRUE;

    return TRUE;
}

BOOL SetLogPath(TCHAR* pLogPath, TCHAR* pModuleName)
{
    if (pLogPath == NULL || _tcslen(pLogPath) == 0) {
        TCHAR szCurrentDir[MAX_PATH] = { 0 };
        GetCurrentDirectory(_countof(szCurrentDir), szCurrentDir);
        _sntprintf_s(g_szLogPath, _countof(g_szLogPath), _T("%s\\Log"), szCurrentDir);
    }
    else {
        _tcsncpy(g_szLogPath, pLogPath, _countof(g_szLogPath));
    }

    TCHAR* pFileName = NULL;
    TCHAR szModuleAbsolutePath[MAX_ABSOLUTEPATH_LEN] = { 0 };
    if (pModuleName == NULL || _tcslen(pModuleName) == 0) {
        //set current path as Module Name
        GetModuleFileName(NULL, szModuleAbsolutePath, _countof(szModuleAbsolutePath));
        pFileName = _tcsrchr(szModuleAbsolutePath, _T('\\'));
        TCHAR* pPointer = _tcsrchr(pFileName, _T('.'));
        *pPointer = _T('\0');
        pFileName++;
    }
    else {
        pFileName = pModuleName;
    }
    _sntprintf_s(g_szModuleName, _countof(g_szModuleName), _T("%s"), pFileName);

    if (CreateDirectory(g_szLogPath, NULL) == ERROR_PATH_NOT_FOUND) {
        return FALSE;
    }

    return TRUE;
}

void DbgMsg(int nLogLevel, const TCHAR* pFmt, ...)
{
    if (g_bInitLog == FALSE && InitLog() == FALSE) {
        return;
    }

//#ifndef _DEBUG
//    if (nLogLevel == LOG_DEBUG) {
//        return;
//    }
//#endif
    if (pFmt == NULL) {
        return;
    }

    TCHAR szVaMsg[MAX_VA_LEN] = { 0 };
    va_list vaArgs;
    va_start(vaArgs, pFmt);
    _vsntprintf(szVaMsg, _countof(szVaMsg), pFmt, vaArgs);
    va_end(vaArgs);

    time_t t = time(NULL);
    struct tm* st = localtime(&t);

    //주의! szLogMsg변수가 szVaMsg변수 위쪽에 선언 될 경우 memory corruption이 발생함.
    //TCHAR szLogMsg[MAX_MSG_LEN] = { 0 };
    int logMsgLen = MAX_MSG_LEN;
    TCHAR* pLogMsg = new TCHAR[logMsgLen];
    memset(pLogMsg, 0, logMsgLen * sizeof(TCHAR));

    switch (nLogLevel)
    {
    case LOG_DEBUG:
        _sntprintf(pLogMsg, logMsgLen, _T("<D>[%02d/%02d %02d:%02d:%02d] %s\r\n"),
            st->tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec, szVaMsg);
        break;
    case LOG_INFO:
        _sntprintf(pLogMsg, logMsgLen, _T("<I>[%02d/%02d %02d:%02d:%02d] %s\r\n"),
            st->tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec, szVaMsg);
        break;
    case LOG_WARNING:
        _sntprintf(pLogMsg, logMsgLen, _T("<W>[%02d/%02d %02d:%02d:%02d] %s\r\n"),
            st->tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec, szVaMsg);
        break;
    case LOG_ERROR:
        _sntprintf(pLogMsg, logMsgLen, _T("<E>[%02d/%02d %02d:%02d:%02d] %s\r\n"),
            st->tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec, szVaMsg);
        break;
    }

    _sntprintf(g_szAbsoluteLogPath, _countof(g_szAbsoluteLogPath), _T("%s\\%04d%02d%02d_%s.log"),
        g_szLogPath, st->tm_year + 1900, st->tm_mon + 1, st->tm_mday, g_szModuleName);

    PostThreadMessage(g_WriteLogThreadID, UM_WRITELOG, (WPARAM)pLogMsg, (LPARAM)(_tcslen(pLogMsg))*sizeof(TCHAR));
    WaitForSingleObject(g_hWSLogEvent, 1000);
}

UINT DeleteLog_Thread(LPVOID pParam)
{
    CTimeSpan tsWeekAgo(7, 0, 0, 0);
    CTime timeCurrent = CTime::GetCurrentTime();
    CTime timeWeeksAgo = timeCurrent - tsWeekAgo;

    CString strLogPath = g_szLogPath;
    CString strModuleName = g_szModuleName;
    CString strBufFile = strLogPath + _T("\\*_") + strModuleName + _T(".log");
    CString strTime = timeWeeksAgo.Format(_T("%Y%m%d")) + _T("_") + strModuleName;
    CString strFileName;

    CFileFind finder;
    BOOL isWorking = finder.FindFile(strBufFile);
    while (isWorking)
    {
        strFileName.Empty();
        isWorking = finder.FindNextFile();
        strFileName = finder.GetFileName();
        if (strTime > strFileName) {
            DeleteFile(strLogPath + _T("\\") + strFileName);
        }
    }

    return 0;
}

UINT WriteLog_Thread(LPVOID pParam)
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (msg.message == UM_WRITELOG) {
            TCHAR* pLogMsg = (TCHAR*)msg.wParam;
            int nLogMsgLen = msg.lParam;
            _tprintf(_T("Log:%s\n"), pLogMsg);

            HANDLE hFile = CreateFile(g_szAbsoluteLogPath,
                GENERIC_WRITE|GENERIC_READ,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                NULL, OPEN_ALWAYS,
                FILE_ATTRIBUTE_NORMAL, NULL);
            if (hFile != INVALID_HANDLE_VALUE) {
                SetFilePointer(hFile, 0, NULL, FILE_END);
                DWORD dwWritten = 0;
                WriteFile(hFile, pLogMsg, nLogMsgLen, &dwWritten, NULL);
            }

            CloseHandle(hFile);

            if (pLogMsg != NULL) {
                delete(pLogMsg);
            }

            SetEvent(g_hWSLogEvent);
        }
        else if (msg.message == UM_TERMINATE_THREAD) {
            g_WriteLogThreadID = -1;
            break;
        }
    }

    return 0;
}

'Programming > Windows Socket Programming' 카테고리의 다른 글

UDP chat server/client  (0) 2020.04.19
IOCP model(Chat Server) - Code  (0) 2020.03.14
IOCP model(Server) - Idea  (0) 2020.03.14
Basic model(Chat Client) - Code  (0) 2020.03.01
Basic model(Client) - Idea  (0) 2020.03.01