# --- logger.py --- import logging import sys # 定義終端機輸出的 ANSI 顏色 COLORS = { "DEBUG": "\033[36m", # 青色 (Cyan) "INFO": "\033[32m", # 綠色 (Green) "WARNING": "\033[33m", # 黃色 (Yellow) "ERROR": "\033[31m", # 紅色 (Red) "CRITICAL": "\033[1;31m",# 粗體紅色 (Bold Red) "RESET": "\033[0m" # 重置顏色 } class ColoredFormatter(logging.Formatter): def format(self, record): log_color = COLORS.get(record.levelname, COLORS["RESET"]) # 格式:時間 | 等級 | 模組名稱 | 訊息 format_str = f"{log_color}%(asctime)s | %(levelname)-7s | %(name)-15s | %(message)s{COLORS['RESET']}" formatter = logging.Formatter(format_str, datefmt="%Y-%m-%d %H:%M:%S") return formatter.format(record) # 定義系統中受管控的模組清單 MANAGED_LOGGERS = [ "app.database", # 資料庫操作 "app.scraper", # 爬蟲與快取 "app.diagnostics", # CM 診斷 "app.backup", # 備份與還原 "app.ssh" # SSH 連線引擎 "app.ssh" # SSH 連線引擎 "app.auth" # 🌟 新增:上帝模式與安全授權 ] def setup_logger(name: str, level=logging.INFO): logger = logging.getLogger(name) logger.setLevel(level) # 避免重複添加 Handler 導致日誌印兩次 if not logger.handlers: handler = logging.StreamHandler(sys.stdout) handler.setFormatter(ColoredFormatter()) logger.addHandler(handler) logger.propagate = False # 防止日誌往上傳遞給 root logger (避免被 FastAPI 預設格式覆蓋) return logger # 系統啟動時,初始化所有模組,預設等級為 INFO for name in MANAGED_LOGGERS: setup_logger(name, logging.INFO) def get_logger(name: str): """供各個檔案引入 logger 使用""" if name not in MANAGED_LOGGERS: return setup_logger(name) return logging.getLogger(name) def get_all_log_levels(): """取得目前所有模組的日誌等級 (供前端 UI 顯示)""" return {name: logging.getLevelName(logging.getLogger(name).level) for name in MANAGED_LOGGERS} def set_log_level(name: str, level_str: str): """動態設定特定模組的日誌等級""" if name in MANAGED_LOGGERS: level = getattr(logging, level_str.upper(), logging.INFO) logging.getLogger(name).setLevel(level) return True return False def register_websocket_handler(handler: logging.Handler): """🌟 新增:註冊自訂 Handler 到所有受管控的 Logger,並防止重複添加""" for name in MANAGED_LOGGERS: logger = logging.getLogger(name) if handler not in logger.handlers: logger.addHandler(handler)