import logging import sys import os from datetime import datetime from logging.handlers import TimedRotatingFileHandler class ViFormatter(logging.Formatter): """Custom formatter for Vi services with symbolic level indicators""" def format(self, record): timestamp = datetime.now().strftime('%H:%M:%S') level_map = { 'DEBUG': '·', 'INFO': '✓', 'WARNING': '⚠', 'ERROR': '✗', 'CRITICAL': '☠' } level_symbol = level_map.get(record.levelname, '?') return f"[{timestamp}] {level_symbol} {record.getMessage()}" def _get_project_root(): """Find the project root directory by looking for core/ directory""" current_dir = os.path.dirname(os.path.abspath(__file__)) return os.path.dirname(current_dir) def setup_logger(name="vi", level=logging.INFO, service_name=None): """Set up a logger with console and optional file output""" # Allow environment variable to override log level env_level = os.getenv('LOG_LEVEL', '').upper() if env_level in ['DEBUG', 'INFO', 'WARNING', 'ERROR']: level = getattr(logging, env_level) logger = logging.getLogger(name) if logger.handlers: return logger logger.setLevel(level) # Console handler console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(ViFormatter()) logger.addHandler(console_handler) # File handler with daily rotation if service_name is specified if service_name: project_root = _get_project_root() logs_dir = os.path.join(project_root, 'logs') if not os.path.exists(logs_dir): os.makedirs(logs_dir, exist_ok=True) log_file = os.path.join(logs_dir, f"{service_name}.log") file_handler = TimedRotatingFileHandler( log_file, when='midnight', interval=1, backupCount=30, encoding='utf-8' ) file_handler.setFormatter(ViFormatter()) logger.addHandler(file_handler) logger.propagate = False return logger logger = setup_logger()