166 lines
4.5 KiB
Python
166 lines
4.5 KiB
Python
"""
|
|
Python Logging Configuration
|
|
Blender addon용 로깅 시스템
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
|
|
# 로그 디렉토리 경로
|
|
def get_log_dir() -> Path:
|
|
"""로그 디렉토리 경로 가져오기"""
|
|
# CLAUDE_PROJECT_DIR 환경변수 또는 현재 작업 디렉토리 사용
|
|
project_dir = os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())
|
|
log_dir = Path(project_dir) / '.blender-toolkit' / 'logs'
|
|
|
|
# 디렉토리 생성
|
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
return log_dir
|
|
|
|
|
|
# 로그 포맷 정의
|
|
LOG_FORMAT = '[%(asctime)s] [%(levelname)-8s] [%(name)s] %(message)s'
|
|
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
|
|
|
|
# 전역 로거 설정 완료 여부
|
|
_logger_initialized = False
|
|
|
|
|
|
def setup_logging(level: int = logging.INFO) -> None:
|
|
"""
|
|
전역 로깅 설정 초기화
|
|
|
|
Args:
|
|
level: 로그 레벨 (logging.DEBUG, INFO, WARNING, ERROR)
|
|
"""
|
|
global _logger_initialized
|
|
|
|
if _logger_initialized:
|
|
return
|
|
|
|
# 로그 디렉토리
|
|
log_dir = get_log_dir()
|
|
|
|
# 루트 로거 설정
|
|
root_logger = logging.getLogger('blender_toolkit')
|
|
root_logger.setLevel(level)
|
|
|
|
# 기존 핸들러 제거 (중복 방지)
|
|
root_logger.handlers.clear()
|
|
|
|
# 파일 핸들러 (모든 로그)
|
|
file_handler = logging.FileHandler(
|
|
log_dir / 'blender-addon.log',
|
|
mode='a',
|
|
encoding='utf-8'
|
|
)
|
|
file_handler.setLevel(level)
|
|
file_handler.setFormatter(logging.Formatter(LOG_FORMAT, DATE_FORMAT))
|
|
|
|
# 파일 핸들러 (에러만)
|
|
error_handler = logging.FileHandler(
|
|
log_dir / 'error.log',
|
|
mode='a',
|
|
encoding='utf-8'
|
|
)
|
|
error_handler.setLevel(logging.ERROR)
|
|
error_handler.setFormatter(logging.Formatter(LOG_FORMAT, DATE_FORMAT))
|
|
|
|
# 콘솔 핸들러 (개발 모드)
|
|
if os.environ.get('DEBUG'):
|
|
console_handler = logging.StreamHandler()
|
|
console_handler.setLevel(logging.DEBUG)
|
|
console_handler.setFormatter(
|
|
logging.Formatter('[%(levelname)-8s] %(message)s')
|
|
)
|
|
root_logger.addHandler(console_handler)
|
|
|
|
# 핸들러 추가
|
|
root_logger.addHandler(file_handler)
|
|
root_logger.addHandler(error_handler)
|
|
|
|
_logger_initialized = True
|
|
|
|
# 초기화 메시지
|
|
root_logger.info('=' * 70)
|
|
root_logger.info(f'Blender Toolkit Logger initialized')
|
|
root_logger.info(f'Log directory: {log_dir}')
|
|
root_logger.info(f'Log level: {logging.getLevelName(level)}')
|
|
root_logger.info('=' * 70)
|
|
|
|
|
|
def get_logger(name: str = None) -> logging.Logger:
|
|
"""
|
|
모듈별 로거 가져오기
|
|
|
|
Args:
|
|
name: 로거 이름 (보통 __name__ 사용)
|
|
|
|
Returns:
|
|
Logger 인스턴스
|
|
|
|
Example:
|
|
```python
|
|
from .utils.logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
logger.info("Hello, world!")
|
|
logger.error("An error occurred", exc_info=True)
|
|
```
|
|
"""
|
|
# 로깅 시스템이 초기화되지 않았으면 초기화
|
|
if not _logger_initialized:
|
|
# DEBUG 환경변수가 있으면 DEBUG 레벨 사용
|
|
level = logging.DEBUG if os.environ.get('DEBUG') else logging.INFO
|
|
setup_logging(level)
|
|
|
|
# 모듈별 로거 반환
|
|
logger_name = f'blender_toolkit.{name}' if name else 'blender_toolkit'
|
|
return logging.getLogger(logger_name)
|
|
|
|
|
|
# 편의 함수들
|
|
def log_function_call(logger: logging.Logger):
|
|
"""
|
|
함수 호출 로깅 데코레이터
|
|
|
|
Example:
|
|
```python
|
|
@log_function_call(logger)
|
|
def my_function(arg1, arg2):
|
|
return arg1 + arg2
|
|
```
|
|
"""
|
|
def decorator(func):
|
|
def wrapper(*args, **kwargs):
|
|
logger.debug(f'Calling {func.__name__}() with args={args}, kwargs={kwargs}')
|
|
try:
|
|
result = func(*args, **kwargs)
|
|
logger.debug(f'{func.__name__}() returned: {result}')
|
|
return result
|
|
except Exception as e:
|
|
logger.error(f'{func.__name__}() raised {type(e).__name__}: {e}', exc_info=True)
|
|
raise
|
|
return wrapper
|
|
return decorator
|
|
|
|
|
|
def log_error(logger: logging.Logger, error: Exception, context: str = None):
|
|
"""
|
|
에러 로깅 헬퍼
|
|
|
|
Args:
|
|
logger: Logger 인스턴스
|
|
error: Exception 객체
|
|
context: 에러 발생 컨텍스트 설명
|
|
"""
|
|
message = f'{type(error).__name__}: {error}'
|
|
if context:
|
|
message = f'{context} - {message}'
|
|
|
|
logger.error(message, exc_info=True)
|