번역툴 2.0 업데이트
This commit is contained in:
193
lib/logger.py
Normal file
193
lib/logger.py
Normal file
@ -0,0 +1,193 @@
|
||||
"""
|
||||
Colored Console Logger for DS_L10N
|
||||
컬러 콘솔 로깅 시스템
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# Windows에서 ANSI 컬러 지원 활성화
|
||||
if sys.platform == 'win32':
|
||||
import os
|
||||
os.system('') # ANSI escape sequences 활성화
|
||||
|
||||
# ANSI 컬러 코드
|
||||
class Colors:
|
||||
RESET = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
|
||||
# 기본 색상
|
||||
BLACK = '\033[30m'
|
||||
RED = '\033[31m'
|
||||
GREEN = '\033[32m'
|
||||
YELLOW = '\033[33m'
|
||||
BLUE = '\033[34m'
|
||||
MAGENTA = '\033[35m'
|
||||
CYAN = '\033[36m'
|
||||
WHITE = '\033[37m'
|
||||
|
||||
# 밝은 색상
|
||||
BRIGHT_RED = '\033[91m'
|
||||
BRIGHT_GREEN = '\033[92m'
|
||||
BRIGHT_YELLOW = '\033[93m'
|
||||
BRIGHT_BLUE = '\033[94m'
|
||||
BRIGHT_MAGENTA = '\033[95m'
|
||||
BRIGHT_CYAN = '\033[96m'
|
||||
BRIGHT_WHITE = '\033[97m'
|
||||
|
||||
# 이모지 아이콘
|
||||
class Icons:
|
||||
SUCCESS = '✅'
|
||||
ERROR = '❌'
|
||||
WARNING = '⚠️'
|
||||
INFO = 'ℹ️'
|
||||
ROCKET = '🚀'
|
||||
GEAR = '⚙️'
|
||||
FILE = '📄'
|
||||
FOLDER = '📁'
|
||||
CHART = '📊'
|
||||
SEARCH = '🔍'
|
||||
CLEAN = '🧹'
|
||||
BACKUP = '💾'
|
||||
CLOCK = '⏱️'
|
||||
|
||||
|
||||
class ColoredFormatter(logging.Formatter):
|
||||
"""컬러 로그 포매터"""
|
||||
|
||||
LEVEL_COLORS = {
|
||||
'DEBUG': Colors.CYAN,
|
||||
'INFO': Colors.BLUE,
|
||||
'WARNING': Colors.YELLOW,
|
||||
'ERROR': Colors.RED,
|
||||
'CRITICAL': Colors.BRIGHT_RED + Colors.BOLD,
|
||||
'SUCCESS': Colors.GREEN,
|
||||
}
|
||||
|
||||
LEVEL_ICONS = {
|
||||
'DEBUG': Icons.GEAR,
|
||||
'INFO': Icons.INFO,
|
||||
'WARNING': Icons.WARNING,
|
||||
'ERROR': Icons.ERROR,
|
||||
'CRITICAL': Icons.ERROR,
|
||||
'SUCCESS': Icons.SUCCESS,
|
||||
}
|
||||
|
||||
def __init__(self, colored=True):
|
||||
super().__init__()
|
||||
self.colored = colored
|
||||
|
||||
def format(self, record):
|
||||
if self.colored:
|
||||
level_color = self.LEVEL_COLORS.get(record.levelname, '')
|
||||
level_icon = self.LEVEL_ICONS.get(record.levelname, '')
|
||||
reset = Colors.RESET
|
||||
|
||||
# 타임스탬프
|
||||
timestamp = datetime.fromtimestamp(record.created).strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 레벨명 포맷
|
||||
level_name = f"{level_color}{record.levelname:<8}{reset}"
|
||||
|
||||
# 메시지
|
||||
message = record.getMessage()
|
||||
|
||||
return f"[{timestamp}] {level_icon} {level_name} {message}"
|
||||
else:
|
||||
timestamp = datetime.fromtimestamp(record.created).strftime('%Y-%m-%d %H:%M:%S')
|
||||
return f"[{timestamp}] [{record.levelname:<8}] {record.getMessage()}"
|
||||
|
||||
|
||||
class DSLogger:
|
||||
"""DS_L10N 전용 로거"""
|
||||
|
||||
def __init__(self, name: str = 'DS_L10N',
|
||||
console_level: str = 'INFO',
|
||||
file_level: str = 'DEBUG',
|
||||
log_file: Optional[Path] = None,
|
||||
colored: bool = True):
|
||||
|
||||
self.logger = logging.getLogger(name)
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
self.logger.handlers.clear() # 기존 핸들러 제거
|
||||
|
||||
# 콘솔 핸들러
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setLevel(getattr(logging, console_level.upper()))
|
||||
console_handler.setFormatter(ColoredFormatter(colored=colored))
|
||||
self.logger.addHandler(console_handler)
|
||||
|
||||
# 파일 핸들러
|
||||
if log_file:
|
||||
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
||||
file_handler.setLevel(getattr(logging, file_level.upper()))
|
||||
file_handler.setFormatter(ColoredFormatter(colored=False))
|
||||
self.logger.addHandler(file_handler)
|
||||
|
||||
# SUCCESS 레벨 추가 (INFO와 WARNING 사이)
|
||||
logging.addLevelName(25, 'SUCCESS')
|
||||
|
||||
def debug(self, message: str):
|
||||
self.logger.debug(message)
|
||||
|
||||
def info(self, message: str):
|
||||
self.logger.info(message)
|
||||
|
||||
def success(self, message: str):
|
||||
self.logger.log(25, message)
|
||||
|
||||
def warning(self, message: str):
|
||||
self.logger.warning(message)
|
||||
|
||||
def error(self, message: str):
|
||||
self.logger.error(message)
|
||||
|
||||
def critical(self, message: str):
|
||||
self.logger.critical(message)
|
||||
|
||||
def separator(self, char: str = '=', length: int = 80):
|
||||
"""구분선 출력"""
|
||||
self.logger.info(char * length)
|
||||
|
||||
def section(self, title: str, icon: str = Icons.ROCKET):
|
||||
"""섹션 제목 출력"""
|
||||
self.separator()
|
||||
self.logger.info(f"{icon} {title}")
|
||||
self.separator()
|
||||
|
||||
def stats(self, **kwargs):
|
||||
"""통계 정보 출력"""
|
||||
self.logger.info(f"{Icons.CHART} 통계:")
|
||||
for key, value in kwargs.items():
|
||||
self.logger.info(f" - {key}: {value}")
|
||||
|
||||
|
||||
# 전역 로거 인스턴스
|
||||
_global_logger: Optional[DSLogger] = None
|
||||
|
||||
|
||||
def get_logger() -> DSLogger:
|
||||
"""전역 로거 가져오기"""
|
||||
global _global_logger
|
||||
if _global_logger is None:
|
||||
_global_logger = DSLogger()
|
||||
return _global_logger
|
||||
|
||||
|
||||
def init_logger(config: dict, log_file: Optional[Path] = None) -> DSLogger:
|
||||
"""로거 초기화"""
|
||||
global _global_logger
|
||||
|
||||
logging_config = config.get('logging', {})
|
||||
|
||||
_global_logger = DSLogger(
|
||||
console_level=logging_config.get('console_level', 'INFO'),
|
||||
file_level=logging_config.get('file_level', 'DEBUG'),
|
||||
log_file=log_file,
|
||||
colored=logging_config.get('colored_output', True)
|
||||
)
|
||||
|
||||
return _global_logger
|
||||
Reference in New Issue
Block a user