| | |
| | """ |
| | 日志工具模块 - 支持时间戳和颜色输出 |
| | """ |
| | import sys |
| | import logging |
| | from datetime import datetime |
| |
|
| | try: |
| | from colorama import init, Fore, Style, Back |
| | init(autoreset=True) |
| | COLORAMA_AVAILABLE = True |
| | except ImportError: |
| | COLORAMA_AVAILABLE = False |
| | |
| | class Fore: |
| | LIGHTBLACK_EX = GREEN = YELLOW = RED = CYAN = BLUE = MAGENTA = WHITE = LIGHTGREEN_EX = LIGHTCYAN_EX = LIGHTYELLOW_EX = LIGHTMAGENTA_EX = "" |
| | class Style: |
| | RESET_ALL = BRIGHT = DIM = "" |
| | class Back: |
| | pass |
| |
|
| |
|
| | class Logger: |
| | """统一日志工具""" |
| |
|
| | SAFE_CHAR_MAP = { |
| | "✓": "[OK] ", |
| | "✗": "[X] ", |
| | "→": "->", |
| | "◆": "*", |
| | } |
| |
|
| | COLORS = { |
| | "DEBUG": Fore.LIGHTBLACK_EX, |
| | "INFO": Fore.GREEN, |
| | "SUCCESS": Fore.LIGHTGREEN_EX, |
| | "WARNING": Fore.YELLOW, |
| | "ERROR": Fore.RED, |
| | "STEP": Fore.CYAN, |
| | "DETAIL": Fore.LIGHTCYAN_EX, |
| | "PROGRESS": Fore.MAGENTA, |
| | "MODEL": Fore.LIGHTMAGENTA_EX, |
| | "AUDIO": Fore.BLUE, |
| | "CONFIG": Fore.LIGHTYELLOW_EX, |
| | } |
| |
|
| | RESET = Style.RESET_ALL |
| | BRIGHT = Style.BRIGHT |
| | DIM = Style.DIM |
| |
|
| | |
| | verbose = True |
| |
|
| | @staticmethod |
| | def _sanitize_console_text(text: str) -> str: |
| | """将不兼容当前终端编码的字符替换为安全文本。""" |
| | sanitized = text |
| | for src, dst in Logger.SAFE_CHAR_MAP.items(): |
| | sanitized = sanitized.replace(src, dst) |
| | return sanitized |
| |
|
| | @staticmethod |
| | def _emit(text: str): |
| | """安全输出到终端,避免 Windows/GBK 控制台因 Unicode 崩溃。""" |
| | try: |
| | print(text, flush=True) |
| | return |
| | except UnicodeEncodeError: |
| | pass |
| |
|
| | fallback = Logger._sanitize_console_text(text) |
| | encoding = getattr(sys.stdout, "encoding", None) or "utf-8" |
| | try: |
| | print( |
| | fallback.encode(encoding, errors="replace").decode(encoding), |
| | flush=True, |
| | ) |
| | except Exception: |
| | print( |
| | fallback.encode("ascii", errors="replace").decode("ascii"), |
| | flush=True, |
| | ) |
| |
|
| | @staticmethod |
| | def _log(level: str, msg: str, force_print: bool = True): |
| | """内部日志方法""" |
| | timestamp = datetime.now().strftime("%H:%M:%S") |
| | color = Logger.COLORS.get(level, "") |
| | reset = Logger.RESET |
| |
|
| | |
| | if level in ("INFO", "STEP", "SUCCESS"): |
| | prefix = "" |
| | elif level == "DETAIL": |
| | prefix = " → " |
| | elif level == "PROGRESS": |
| | prefix = " ◆ " |
| | elif level == "MODEL": |
| | prefix = "[模型] " |
| | elif level == "AUDIO": |
| | prefix = "[音频] " |
| | elif level == "CONFIG": |
| | prefix = "[配置] " |
| | else: |
| | prefix = f"[{level}] " |
| |
|
| | output = f"{color}[{timestamp}]{prefix}{msg}{reset}" |
| | Logger._emit(output) |
| |
|
| | @staticmethod |
| | def debug(msg: str): |
| | """调试日志 (灰色) - 仅在verbose模式下显示""" |
| | if Logger.verbose: |
| | Logger._log("DEBUG", msg) |
| |
|
| | @staticmethod |
| | def info(msg: str): |
| | """信息日志 (绿色)""" |
| | Logger._log("INFO", msg) |
| |
|
| | @staticmethod |
| | def success(msg: str): |
| | """成功日志 (亮绿色)""" |
| | Logger._log("SUCCESS", f"✓ {msg}") |
| |
|
| | @staticmethod |
| | def warning(msg: str): |
| | """警告日志 (黄色)""" |
| | Logger._log("WARNING", msg) |
| |
|
| | @staticmethod |
| | def error(msg: str): |
| | """错误日志 (红色)""" |
| | Logger._log("ERROR", msg) |
| |
|
| | @staticmethod |
| | def step(current: int, total: int, msg: str): |
| | """步骤日志 (青色)""" |
| | timestamp = datetime.now().strftime("%H:%M:%S") |
| | color = Logger.COLORS.get("STEP", "") |
| | reset = Logger.RESET |
| | Logger._emit(f"{color}[{timestamp}][{current}/{total}] {msg}{reset}") |
| |
|
| | @staticmethod |
| | def detail(msg: str): |
| | """详细日志 (浅青色) - 用于显示处理细节""" |
| | if Logger.verbose: |
| | Logger._log("DETAIL", msg) |
| |
|
| | @staticmethod |
| | def progress(msg: str): |
| | """进度日志 (紫色) - 用于显示处理进度""" |
| | Logger._log("PROGRESS", msg) |
| |
|
| | @staticmethod |
| | def model(msg: str): |
| | """模型日志 (浅紫色) - 用于模型加载/卸载信息""" |
| | Logger._log("MODEL", msg) |
| |
|
| | @staticmethod |
| | def audio(msg: str): |
| | """音频日志 (蓝色) - 用于音频处理信息""" |
| | Logger._log("AUDIO", msg) |
| |
|
| | @staticmethod |
| | def config(msg: str): |
| | """配置日志 (浅黄色) - 用于配置信息""" |
| | if Logger.verbose: |
| | Logger._log("CONFIG", msg) |
| |
|
| | @staticmethod |
| | def header(msg: str): |
| | """标题日志 (带分隔线)""" |
| | timestamp = datetime.now().strftime("%H:%M:%S") |
| | color = Logger.COLORS.get("INFO", "") |
| | reset = Logger.RESET |
| | Logger._emit(f"{color}[{timestamp}] {'=' * 50}{reset}") |
| | Logger._emit(f"{color}[{timestamp}] {msg}{reset}") |
| | Logger._emit(f"{color}[{timestamp}] {'=' * 50}{reset}") |
| |
|
| | @staticmethod |
| | def separator(char: str = "-", length: int = 40): |
| | """分隔线""" |
| | timestamp = datetime.now().strftime("%H:%M:%S") |
| | color = Logger.COLORS.get("DEBUG", "") |
| | reset = Logger.RESET |
| | Logger._emit(f"{color}[{timestamp}] {char * length}{reset}") |
| |
|
| | @staticmethod |
| | def set_verbose(enabled: bool): |
| | """设置详细日志模式""" |
| | Logger.verbose = enabled |
| |
|
| |
|
| | |
| | log = Logger() |
| |
|
| |
|
| | |
| |
|
| | class ColoredFormatter(logging.Formatter): |
| | """为标准logging模块添加颜色支持""" |
| |
|
| | LEVEL_COLORS = { |
| | logging.DEBUG: Fore.LIGHTBLACK_EX, |
| | logging.INFO: Fore.GREEN, |
| | logging.WARNING: Fore.YELLOW, |
| | logging.ERROR: Fore.RED, |
| | logging.CRITICAL: Fore.RED + Style.BRIGHT, |
| | } |
| |
|
| | def format(self, record): |
| | |
| | color = self.LEVEL_COLORS.get(record.levelno, "") |
| | reset = Style.RESET_ALL |
| |
|
| | |
| | timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
| |
|
| | |
| | level_name = record.levelname |
| | module_name = record.name |
| |
|
| | |
| | formatted = f"{color}{timestamp} | {level_name} | {module_name} | {record.getMessage()}{reset}" |
| | return formatted |
| |
|
| |
|
| | def setup_colored_logging(level=logging.INFO): |
| | """配置全局logging使用颜色输出""" |
| | |
| | root_logger = logging.getLogger() |
| | root_logger.setLevel(level) |
| |
|
| | |
| | for handler in root_logger.handlers[:]: |
| | root_logger.removeHandler(handler) |
| |
|
| | |
| | console_handler = logging.StreamHandler(sys.stdout) |
| | console_handler.setLevel(level) |
| | console_handler.setFormatter(ColoredFormatter()) |
| | root_logger.addHandler(console_handler) |
| |
|
| | return root_logger |
| |
|
| |
|
| | |
| | setup_colored_logging(logging.INFO) |
| |
|
| | |
| | logging.getLogger("faiss").setLevel(logging.WARNING) |
| | logging.getLogger("audio_separator").setLevel(logging.WARNING) |
| |
|