포스트

python 프로그램 커스텀 로깅 클래스 만들기(디테일 버전)


python 디폴트 로거 관련 포스팅은 다음을 참조해주세요. ** https://blog.naver.com/devramyun/223749582224

[20250206] python 프로그램 로그 저장 파이썬 실행 중에 터미널에 출력되는 모든 로그(표준 출력 및 표준 오류)를 파일로 저장하는 간단한 방법이… 파이썬 실행 중에 터미널에 출력되는 모든 로그(표준 출력 및 표준 오류)를 파일로 저장하는 간단한 방법이…


이전 포스팅에서 python 프로그램 커스텀 로깅 클래스 만들기를 했었습니다.

https://blog.naver.com/devramyun/223749590587

[20250206] python 프로그램 커스텀 로깅 클래스 만들기(라이트 버전) 자세한 로깅 관련 내용은 이전 포스팅을 참조해주세요. https://blog.naver.com/devramyun/223749582224 위… 자세한 로깅 관련 내용은 이전 포스팅을 참조해주세요. https://blog.naver.com/devramyun/223749582224 위…


바로 logging 패키지를 커스터마이징해서 사용했었습니다.

그런데 이 로거의 단점은 터미널과 완전히 동일한 메시지를 로그파일에 저장할 수는 없다는 한계가 있었습니다.

예를 들어 다음과 같은 상황에서 터미널의 출력에는 있지만 로그에는 저장 안되는 부분이 존재합니다.

그래서 터미널과 동일하게 저장되도록 하기 위해서 새로운 버전의 커스텀 로깅 클래스를 만들게 되었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# logger2.py

import sys
import os
from datetime import datetime

# ✅ 실행 시간 기반으로 로그 파일 생성
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)  # 로그 저장 폴더 생성
log_filename = os.path.join(log_dir, f"log_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log")

log_file = open(log_filename, "w", encoding="utf-8")


class SingletonMeta(type):
    """싱글톤 메타클래스"""

    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class MyLogger2(metaclass=SingletonMeta):
    """터미널과 파일에 동시에 출력하는 싱글톤 로거 (MyLogger와 호환)"""

    def __init__(self, file=None, name: str = "default"):
        if not hasattr(self, "file"):  # 최초 초기화 방지
            self.file = file if file else sys.stdout  # 파일 핸들
            self.terminal = sys.stdout  # 원래 터미널 저장

    def write(self, message):
        """터미널과 파일에 동시에 출력"""
        self.terminal.write(message)
        self.file.write(message)

    def flush(self):
        """출력 버퍼 플러시"""
        self.terminal.flush()
        self.file.flush()

    def info(self, message: str):
        self.write(f"[INFO] {message}\n")

    def warning(self, message: str):
        self.write(f"[WARNING] {message}\n")

    def error(self, message: str):
        self.write(f"[ERROR] {message}\n")

    def exception(self, message: str):
        self.write(f"[EXCEPTION] {message}\n")


# ✅ 싱글톤 로거 인스턴스 생성
myLogger = MyLogger2(log_file)

# ✅ `sys.stdout`과 `sys.stderr`을 싱글톤 인스턴스로 리디렉션
sys.stdout = myLogger
sys.stderr = myLogger  # 오류도 같은 방식으로 처리 (traceback 포함)

if __name__ == "__main__":
    # ✅ 테스트 출력
    print("이것은 터미널과 파일에 동시에 출력됩니다.")

    myLogger.info("INFO 테스트 메시지")
    myLogger.warning("WARNING 테스트 메시지")
    myLogger.error("ERROR 테스트 메시지")

    # ✅ 예제 예외 발생 (로그 파일에도 저장됨)
    try:
        raise ValueError("예제 오류 발생")
    except ValueError:
        myLogger.exception("예외 발생!")

사용법은 그냥 import 해서 쓰시면 됩니다.

1
logger2 import myLogger

다시 테스트 해보면 로그가 동일하게 기록됩니다.

이 새로운 방법의 장점은,

pyinstaller로 빌드해서 exe를 실행할 때 exception을 잡지 못한 에러가 발생해도

터미널에 출력된 그대로를 로그에 저장하여 볼 수 있다는 점입니다.

sticker

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.