파이썬(Python)에서 개발, 디버깅 및 모니터링을 위해 콘솔 및 파일 로깅이 필요하다. 로깅에 필요한 요구사항을 정리해보고, 표준 라이브러리인 Logging 모듈을 이용해 콘솔 색상출력, 파일 저장 및 분할(로테이션)이 가능한 파이썬 로그 클래스를 구현해보고, 코드와 사용 예제를 공유해 본다.
파이썬 로그 요구사항
- 날짜 및 시간, 태그, 로깅 레벨, 메시지 형식을 지원한다.
- 로깅 레벨은 DEBUG, INFO, WARNING, ERROR, CRITICAL의 5단계를 지원한다.
- 시인성을 위해 콘솔에서 로깅 레벨별 색상 출력을 지원한다.
- 로그 파일을 지원하며, 로깅 레벨 지정이 가능하다.
- 로그 파일은 5MB 단위로 로테이션을 지원한다.
파이썬 로그 구현
파이썬 로그 요구사항에 맞춰 아래와 같이 코드를 작성하고 log.py로 저장한다.
'''
from log import Log
Log.init(log_filename="logs/test.log", file_log_level="WARNING", tag_len=14)
TAG = "MyApp"
Log.d(TAG, "디버그 메시지")
Log.i(TAG, "정보 메시지")
Log.w(TAG, "경고 메시지")
Log.e(TAG, "에러 메시지")
Log.c("CriticalMessageTest", "크리티컬 메시지")
Log.d(TAG, f"로그저장 위치: {Log.get_log_file_path()} ")
'''
import logging
import os
import inspect
from logging.handlers import RotatingFileHandler
class ColorFormatter(logging.Formatter):
COLOR_MAP = {
'D': '\033[94m', # DEBUG - 파랑
'I': '\033[92m', # INFO - 초록
'W': '\033[93m', # WARNING - 노랑
'E': '\033[91m', # ERROR - 빨강
'C': '\033[95m', # CRITICAL - 자주
}
RESET = '\033[0m'
def format(self, record):
level_short = {
'DEBUG': 'D',
'INFO': 'I',
'WARNING': 'W',
'ERROR': 'E',
'CRITICAL': 'C'
}.get(record.levelname, '?')
record.levelshort = level_short
# 태그 처리, Log._tag_len에 지정된 글자수로 공백 패딩 또는 말줄임표 처리
raw_tag = getattr(record, 'tag', 'NO_TAG')
if len(raw_tag) > Log._tag_len:
record.tag = raw_tag[:Log._tag_len-3] + "..."
else:
record.tag = raw_tag.ljust(Log._tag_len) # 왼쪽 정렬 + 공백 채우기
color = self.COLOR_MAP.get(level_short, '')
formatted = super().format(record)
return f"{color}{formatted}{self.RESET}"
class Log:
_logger_initialized = False
_log_file_path = None
_tag_len = 14
@classmethod
def init(cls, log_filename=None, file_log_level="DEBUG", tag_len=10):
if cls._logger_initialized:
return
cls._tag_len = tag_len
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 콘솔 핸들러
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(ColorFormatter('%(asctime)s [%(tag)s] [%(levelshort)s] %(message)s')) # 색상 지정
logger.addHandler(stream_handler)
# 파일 핸들러
if log_filename:
cls._log_file_path = log_filename
log_dir = os.path.dirname(log_filename)
if log_dir and not os.path.exists(log_dir):
os.makedirs(log_dir)
try:
level = getattr(logging, file_log_level.upper())
except AttributeError:
level = logging.DEBUG
file_handler = RotatingFileHandler(
log_filename,
maxBytes=5 * 1024 * 1024, # 5MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(level)
class PlainFormatter(logging.Formatter):
def format(self, record):
level_short = {
'DEBUG': 'D',
'INFO': 'I',
'WARNING': 'W',
'ERROR': 'E',
'CRITICAL': 'C'
}.get(record.levelname, '?')
record.levelshort = level_short
return super().format(record)
file_handler.setFormatter(PlainFormatter('%(asctime)s [%(tag)s] [%(levelshort)s] %(message)s')) # 색장 지정 없음
logger.addHandler(file_handler)
cls._logger_initialized = True
def _get_caller_function_name():
return inspect.currentframe().f_back.f_back.f_code.co_name
@staticmethod
def _extract_tag_and_msg(*args):
if len(args) == 1:
# TAG 생략 → 함수명 자동 추출
tag = inspect.currentframe().f_back.f_back.f_code.co_name
msg = args[0]
elif len(args) == 2:
tag, msg = args
else:
raise ValueError("Log 메서드는 message만 주거나 tag, message 두 개를 줘야 합니다.")
return tag, msg
@classmethod
def d(cls, *args):
tag, msg = cls._extract_tag_and_msg(*args)
logging.getLogger().debug(msg, extra={'tag': tag})
@classmethod
def i(cls, *args):
tag, msg = cls._extract_tag_and_msg(*args)
logging.getLogger().info(msg, extra={'tag': tag})
@classmethod
def w(cls, *args):
tag, msg = cls._extract_tag_and_msg(*args)
logging.getLogger().warning(msg, extra={'tag': tag})
@classmethod
def e(cls, *args):
tag, msg = cls._extract_tag_and_msg(*args)
logging.getLogger().error(msg, extra={'tag': tag})
@classmethod
def c(cls, *args):
tag, msg = cls._extract_tag_and_msg(*args)
logging.getLogger().critical(msg, extra={'tag': tag})
@classmethod
def get_log_file_path(cls):
return cls._log_file_path
파이썬 로그 사용 예제
로깅이 필요한 파이썬 파일에 작성한 로그 모듈(log.py)을 임포트한다.
from log import Log
로그를 초기화한다. 로그 파일 저장이 필요하면 log_filename을 지정한다. 지정한 디렉터리가 없을 경우 자동으로 생성한다. 특정 로깅 레벨 및 그 상위 레벨만 파일로 저장하고 싶다면 file_log_level에 지정한다.
예를 들어 file_log_level을 WARNING으로 지정하면 WARNING, ERROR, CRITICAL만 파일 로그로 남게 된다.
tag를 생략할 경우, 로그가 위치한 함수명을 자동 추출하여 태그명으로 표시하도록 하였다.
tag의 길이가 일정하지 않으면 콘솔 출력 시 보기가 좋지 않기 때문에 tag_len을 지정해, 지정한 길이 이상일 경우에는 말줄임표(…)로 표시하도록 하였다.
Log.init(log_filename="logs/test.log", file_log_level="WARNING", tag_len=14)
추가로, 로깅 파일이 커질 경우 로테이션이 필요하다. 로테이션은 5MB마다, 백업 카운트는 5회로 코드 내에 하드코딩해 두었다. 필요한 경우 값을 수정하여 사용하면 된다.
file_handler = RotatingFileHandler(
log_filename,
maxBytes=5 * 1024 * 1024, # 5MB
backupCount=5,
encoding='utf-8'
)
다음과 같이 백업 파일이 5개까지 생성되며, 초과될 경우 .5 파일이 삭제되고 .4가 .5로 밀린다.
logs/
├── test.log ← 현재 로그
├── test.log.1 ← 최근 백업
├── test.log.2
├── test.log.3
├── test.log.4
├── test.log.5 ← 가장 오래된 백업
로그 출력 코드를 다음과 같이 작성하였을 때, 콘솔 출력과 저장된 로그 파일은 다음과 같다.
TAG = "MyApp"
Log.d(TAG, "디버그 메시지")
Log.i(TAG, "정보 메시지")
Log.w(TAG, "경고 메시지")
Log.e(TAG, "에러 메시지")
Log.c("CriticalMessageTest", "크리티컬 메시지")
Log.d(TAG, f"로그저장 위치: {Log.get_log_file_path()} ")


마치며
간단한 로깅이 필요할 때는 print() 함수로도 충분할 수 있지만, 프로젝트 규모가 커지고 유지보수나 운영 환경에서 진단이 중요해질수록 체계적인 로깅 시스템이 필요해진다.
이번 글에서는 파이썬 표준 라이브러리인 logging 모듈을 기반으로, 콘솔 색상 출력과 로그 파일 로테이션까지 포함한 실용적인 파이썬 로그 클래스를 구현해 보았다.
직접 구현해보면서 로깅의 기본 개념과 구조를 이해하는 데 도움이 되었기를 바란다.
필요에 따라 파이썬 로그 클래스에 슬랙 연동이나 로그 필터링 등 추가 기능을 붙이는 것도 어렵지 않으니, 이 코드를 기반으로 자신만의 로깅 도구로 확장해보는 것도 추천한다.