# =================================================================== # core/logging_utils.py # FGTools 로깅 유틸리티 모듈 # =================================================================== # 일관된 로그 포맷과 설정을 제공하는 로깅 유틸리티입니다. # 파일 및 콘솔 출력, 로그 레벨 설정을 지원합니다. # =================================================================== """ 로깅 유틸리티 모듈 일관된 로그 포맷과 핸들러 설정을 제공합니다. 콘솔 출력, 파일 저장, 로그 로테이션을 지원합니다. 사용 예시: from core.logging_utils import get_logger, setup_logging # 간단한 로거 사용 logger = get_logger(__name__) logger.info("메시지") # 상세 설정이 필요한 경우 logger = setup_logging("my_module", level="DEBUG", log_file="app.log") """ import os import sys import logging from typing import Optional from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler from .config import get_config # 로그 포맷 상수 DEFAULT_FORMAT = '[%(asctime)s] %(name)s - %(levelname)s: %(message)s' DETAILED_FORMAT = '[%(asctime)s] %(name)s (%(filename)s:%(lineno)d) - %(levelname)s: %(message)s' DATE_FORMAT = '%Y-%m-%d %H:%M:%S' # 로그 디렉토리 LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'logs') def _ensure_log_dir(): """로그 디렉토리 생성""" if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR, exist_ok=True) def setup_logging( name: str, level: Optional[str] = None, log_file: Optional[str] = None, log_format: str = DEFAULT_FORMAT, max_bytes: int = 10 * 1024 * 1024, # 10MB backup_count: int = 5, console_output: bool = True ) -> logging.Logger: """ 로거 설정 및 반환 일관된 포맷으로 로거를 설정합니다. 콘솔 출력과 파일 저장을 동시에 지원하며, 로그 로테이션을 자동으로 처리합니다. Args: name: 로거 이름 (보통 __name__ 사용) level: 로그 레벨 (DEBUG, INFO, WARNING, ERROR, CRITICAL) None이면 환경 설정에서 로드 log_file: 로그 파일명 (None이면 파일 저장 안 함) log_format: 로그 메시지 포맷 max_bytes: 로그 파일 최대 크기 (로테이션 기준) backup_count: 보관할 백업 파일 수 console_output: 콘솔 출력 여부 Returns: 설정된 Logger 인스턴스 """ # 설정에서 기본 레벨 로드 if level is None: try: level = get_config().log_level except Exception: level = 'INFO' logger = logging.getLogger(name) # 이미 핸들러가 있으면 레벨만 설정하고 반환 if logger.handlers: logger.setLevel(getattr(logging, level.upper(), logging.INFO)) return logger # 로그 레벨 설정 log_level = getattr(logging, level.upper(), logging.INFO) logger.setLevel(log_level) # 포매터 생성 formatter = logging.Formatter(log_format, datefmt=DATE_FORMAT) # 콘솔 핸들러 추가 if console_output: console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(log_level) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # 파일 핸들러 추가 (요청 시) if log_file: _ensure_log_dir() file_path = os.path.join(LOG_DIR, log_file) file_handler = RotatingFileHandler( file_path, maxBytes=max_bytes, backupCount=backup_count, encoding='utf-8' ) file_handler.setLevel(log_level) file_handler.setFormatter(formatter) logger.addHandler(file_handler) # 부모 로거로 전파 방지 logger.propagate = False return logger def get_logger(name: str) -> logging.Logger: """ 간편 로거 반환 기본 설정으로 로거를 반환합니다. 이미 설정된 로거가 있으면 재사용합니다. Args: name: 로거 이름 (보통 __name__ 사용) Returns: Logger 인스턴스 """ return setup_logging(name) def setup_file_logger( name: str, log_file: str, level: str = 'INFO', rotation: str = 'size', # 'size' 또는 'time' when: str = 'midnight', # rotation='time'일 때 사용 interval: int = 1, # rotation='time'일 때 사용 ) -> logging.Logger: """ 파일 전용 로거 설정 콘솔 출력 없이 파일에만 로그를 기록합니다. 크기 기반 또는 시간 기반 로테이션을 선택할 수 있습니다. Args: name: 로거 이름 log_file: 로그 파일명 level: 로그 레벨 rotation: 로테이션 방식 ('size' 또는 'time') when: 시간 기반 로테이션 주기 (midnight, H, D, W0-W6) interval: 로테이션 간격 Returns: 설정된 Logger 인스턴스 """ _ensure_log_dir() logger = logging.getLogger(name) if logger.handlers: return logger log_level = getattr(logging, level.upper(), logging.INFO) logger.setLevel(log_level) formatter = logging.Formatter(DETAILED_FORMAT, datefmt=DATE_FORMAT) file_path = os.path.join(LOG_DIR, log_file) if rotation == 'time': # 시간 기반 로테이션 (예: 매일 자정) handler = TimedRotatingFileHandler( file_path, when=when, interval=interval, backupCount=30, encoding='utf-8' ) else: # 크기 기반 로테이션 handler = RotatingFileHandler( file_path, maxBytes=10 * 1024 * 1024, # 10MB backupCount=5, encoding='utf-8' ) handler.setLevel(log_level) handler.setFormatter(formatter) logger.addHandler(handler) logger.propagate = False return logger class LogContext: """ 로그 컨텍스트 관리자 특정 작업의 시작과 종료를 자동으로 로깅합니다. 작업 소요 시간도 함께 기록됩니다. 사용 예시: with LogContext(logger, "데이터 처리"): process_data() # 출력: # [시작] 데이터 처리 # [완료] 데이터 처리 (소요시간: 1.23초) """ def __init__(self, logger: logging.Logger, task_name: str, level: int = logging.INFO): """ Args: logger: Logger 인스턴스 task_name: 작업 이름 level: 로그 레벨 """ self.logger = logger self.task_name = task_name self.level = level self.start_time = None def __enter__(self): """작업 시작 로깅""" import time self.start_time = time.time() self.logger.log(self.level, f"[시작] {self.task_name}") return self def __exit__(self, exc_type, exc_val, exc_tb): """작업 종료 로깅""" import time elapsed = time.time() - self.start_time if exc_type is not None: self.logger.error(f"[실패] {self.task_name} - {exc_type.__name__}: {exc_val}") else: self.logger.log(self.level, f"[완료] {self.task_name} (소요시간: {elapsed:.2f}초)") return False # 예외 전파