본문 바로가기
코딩취미/C,C++

MFC에서 로그 파일 관리: 카테고리 구분 + 자동 파일 분할 기능 구현하기

by 브링블링 2025. 4. 7.
반응형

MFC에서 로그 파일 관리: 카테고리 구분 + 자동 파일 분할 기능 구현하기

MFC 기반 애플리케이션에서 작업의 진행 상태나 오류 내용을 기록할 때 텍스트 로그 파일을 활용하는 경우가 많습니다. 하지만 로그가 누적되면 파일이 점점 커지고, 열기조차 버거워지는 문제가 발생하죠.

이번 포스트에서는 아래 기능을 포함한 깔끔한 로그 시스템을 직접 구현해보겠습니다:


✅ 구현 목표

  1. 로그 카테고리 구분 (예: INFO, DEBUG, ERROR 등)
  2. 로그 파일 경로 직접 지정 가능
  3. 어디서든 호출할 수 있도록 함수화
  4. 파일 크기 초과 시 자동 분할 및 백업

🛠️ 핵심 함수 설계

🔸 함수 시그니처

void WriteLog(CString category, CString message, CString logFilePath = _T("C:\\Logs\\my_app_log.txt"), ULONGLONG maxFileSize = 1024 * 1024);
  • category: 로그의 종류 (예: "INFO", "ERROR")
  • message: 저장할 메시지
  • logFilePath: 로그 파일 경로 (기본값 사용 가능)
  • maxFileSize: 최대 파일 크기 (기본값: 1MB)

📄 코드 전체

🔹 LogUtils.h

#pragma once

void WriteLog(CString category, CString message, CString logFilePath = _T("C:\\Logs\\my_app_log.txt"), ULONGLONG maxFileSize = 1024 * 1024);

🔹 LogUtils.cpp

#include "pch.h"
#include "LogUtils.h"
#include <afx.h>
#include <afxstr.h>
#include <sys/stat.h>
#include <io.h>

void WriteLog(CString category, CString message, CString logFilePath, ULONGLONG maxFileSize)
{
    // 로그 폴더 생성
    int pos = logFilePath.ReverseFind(_T('\\'));
    if (pos != -1)
    {
        CString folderPath = logFilePath.Left(pos);
        _wmkdir(folderPath);
    }

    // 파일 크기 초과 시 백업
    CFileStatus status;
    if (CFile::GetStatus(logFilePath, status))
    {
        if (status.m_size >= maxFileSize)
        {
            CTime now = CTime::GetCurrentTime();
            CString timestamp = now.Format(_T("_%Y%m%d_%H%M%S"));

            int dot = logFilePath.ReverseFind(_T('.'));
            CString newBackupPath;
            if (dot != -1)
                newBackupPath = logFilePath.Left(dot) + timestamp + logFilePath.Mid(dot);
            else
                newBackupPath = logFilePath + timestamp;

            ::MoveFile(logFilePath, newBackupPath);
        }
    }

    // 로그 메시지 생성
    CTime currentTime = CTime::GetCurrentTime();
    CString timeStr = currentTime.Format(_T("%Y-%m-%d %H:%M:%S"));

    CString logLine;
    logLine.Format(_T("[%s] [%s] %s\n"), timeStr, category, message);

    // 파일에 기록
    CStdioFile file;
    if (file.Open(logFilePath, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite | CFile::typeText))
    {
        file.SeekToEnd();
        file.WriteString(logLine);
        file.Close();
    }
}
반응형

🧪 사용 예시

#include "LogUtils.h"

void CMyDialog::OnBnClickedStart()
{
    WriteLog(_T("INFO"), _T("작업 시작"));

    for (int i = 0; i < 5; ++i)
    {
        CString msg;
        msg.Format(_T("작업 진행 중: %d단계"), i + 1);
        WriteLog(_T("DEBUG"), msg);
        Sleep(300);
    }

    WriteLog(_T("INFO"), _T("작업 완료"));
}

📁 결과 예시 (로그 출력)

[2025-04-07 13:05:01] [INFO] 작업 시작
[2025-04-07 13:05:01] [DEBUG] 작업 진행 중: 1단계
[2025-04-07 13:05:01] [DEBUG] 작업 진행 중: 2단계
[2025-04-07 13:05:02] [DEBUG] 작업 진행 중: 3단계
[2025-04-07 13:05:02] [DEBUG] 작업 진행 중: 4단계
[2025-04-07 13:05:03] [DEBUG] 작업 진행 중: 5단계
[2025-04-07 13:05:03] [INFO] 작업 완료

🔄 변경될 기능 요약

  • 기존 하나의 파일에 저장 → 로그 레벨별 다른 파일로 저장
  • 예시:
    • C:\Logs\info_log.txt
    • C:\Logs\error_log.txt
    • C:\Logs\debug_log.txt
  • 백업도 각 파일별로 자동 분할 및 보관

✍️ 업데이트된 핵심 코드

🔹 함수 시그니처 (변경 없음)

void WriteLog(CString category, CString message, CString logDir = _T("C:\\Logs"), ULONGLONG maxFileSize = 1024 * 1024);

🔹 LogUtils.h

#pragma once

void WriteLog(CString category, CString message, CString logDir = _T("C:\\Logs"), ULONGLONG maxFileSize = 1024 * 1024);

🔹 LogUtils.cpp

#include "pch.h"
#include "LogUtils.h"
#include <afx.h>
#include <afxstr.h>
#include <sys/stat.h>
#include <io.h>

CString GetLogFilePathByCategory(const CString& category, const CString& logDir)
{
    CString lowerCat = category;
    lowerCat.MakeLower();  // INFO → info
    return logDir + _T("\\") + lowerCat + _T("_log.txt");
}

void WriteLog(CString category, CString message, CString logDir, ULONGLONG maxFileSize)
{
    // 로그 디렉터리 확인 및 생성
    _wmkdir(logDir);

    CString logFilePath = GetLogFilePathByCategory(category, logDir);

    // 파일 크기 초과 시 백업
    CFileStatus status;
    if (CFile::GetStatus(logFilePath, status))
    {
        if (status.m_size >= maxFileSize)
        {
            CTime now = CTime::GetCurrentTime();
            CString timestamp = now.Format(_T("_%Y%m%d_%H%M%S"));

            int dot = logFilePath.ReverseFind(_T('.'));
            CString backupPath;
            if (dot != -1)
                backupPath = logFilePath.Left(dot) + timestamp + logFilePath.Mid(dot);
            else
                backupPath = logFilePath + timestamp;

            ::MoveFile(logFilePath, backupPath);
        }
    }

    // 로그 메시지 작성
    CTime currentTime = CTime::GetCurrentTime();
    CString timeStr = currentTime.Format(_T("%Y-%m-%d %H:%M:%S"));
    CString logLine;
    logLine.Format(_T("[%s] [%s] %s\n"), timeStr, category, message);

    // 로그 쓰기
    CStdioFile file;
    if (file.Open(logFilePath, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite | CFile::typeText))
    {
        file.SeekToEnd();
        file.WriteString(logLine);
        file.Close();
    }
}

🧪 사용 예시

WriteLog(_T("INFO"), _T("애플리케이션 시작"));
WriteLog(_T("DEBUG"), _T("디버그 값: x = 42"));
WriteLog(_T("ERROR"), _T("파일 열기 실패"));

📁 생성되는 파일 구조 (예시)

C:\Logs\
    ├── info_log.txt
    ├── debug_log.txt
    ├── error_log.txt
    ├── info_log_20250407_140312.txt (백업)

✅ 마무리 요약

이제 로그는 카테고리별로 다음과 같이 나눠집니다:

  • 📘 INFO → info_log.txt
  • 🔧 DEBUG → debug_log.txt
  • ❌ ERROR → error_log.txt

그리고 각 파일은 지정한 용량(예: 1MB)을 넘으면 자동으로 백업되며 새 파일로 이어집니다.

반응형