반응형
MFC에서 로그 파일 관리: 카테고리 구분 + 자동 파일 분할 기능 구현하기
MFC 기반 애플리케이션에서 작업의 진행 상태나 오류 내용을 기록할 때 텍스트 로그 파일을 활용하는 경우가 많습니다. 하지만 로그가 누적되면 파일이 점점 커지고, 열기조차 버거워지는 문제가 발생하죠.
이번 포스트에서는 아래 기능을 포함한 깔끔한 로그 시스템을 직접 구현해보겠습니다:
✅ 구현 목표
- 로그 카테고리 구분 (예: INFO, DEBUG, ERROR 등)
- 로그 파일 경로 직접 지정 가능
- 어디서든 호출할 수 있도록 함수화
- 파일 크기 초과 시 자동 분할 및 백업
🛠️ 핵심 함수 설계
🔸 함수 시그니처
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)을 넘으면 자동으로 백업되며 새 파일로 이어집니다.
반응형
'코딩취미 > C,C++' 카테고리의 다른 글
C# 코드 정리 방법: 초보자부터 숙련자까지 (SOLID) (0) | 2025.03.02 |
---|---|
초보자를 위한 C언어 가변 인자 함수: va_list, va_start, va_end 완벽 가이드 (0) | 2024.09.13 |
파일 입출력 초보 탈출! C언어 fopen_s 사용법 정리 (+ fopen 비교) (0) | 2024.09.12 |
InvokeRequired를 사용하는 이유 (사용해야 할 상황 + 사용하면 안되는 상황) (0) | 2024.08.28 |
프로그래밍 goto의 오해와 진실, 구조적 패턴 10가지 (0) | 2024.08.27 |