Files
TransPyC/lib/core/VLogger.py
2026-07-18 19:25:40 +08:00

306 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
TransPyC 彩色日志系统
支持终端颜色输出和日志文件转储
"""
from __future__ import annotations
import sys
import os
from datetime import datetime
from typing import Any, Callable, Optional
from lib.constants.config import mode as _ConfigMode
# ANSI 颜色代码
class Colors:
RESET = '\033[0m'
BOLD = '\033[1m'
DIM = '\033[2m'
UNDERLINE = '\033[4m'
# 前景色
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'
# 背景色
BG_RED = '\033[41m'
BG_GREEN = '\033[42m'
BG_YELLOW = '\033[43m'
BG_BLUE = '\033[44m'
BG_MAGENTA = '\033[45m'
BG_CYAN = '\033[46m'
# 渐变颜色(用于详细日志)
GRAY = '\033[90m'
# 检查是否支持颜色
def supports_color() -> bool:
if not hasattr(sys.stdout, 'isatty'):
return False
if not sys.stdout.isatty():
return False
if os.environ.get('NO_COLOR'):
return False
if os.environ.get('FORCE_COLOR'):
return True
return True
# 全局颜色支持标志
USE_COLORS = supports_color()
# 日志级别
class LogLevel:
DEBUG = 0
INFO = 1
WARNING = 2
ERROR = 3
CRITICAL = 4
class Logger:
def __init__(self, name: str = "TransPyC", log_file: Optional[str] = None,
level: int = LogLevel.INFO, use_colors: bool = True):
self.name = name
self.level = level
self.use_colors = use_colors and USE_COLORS
self.log_file = log_file
self.file_handler = None
if log_file:
os.makedirs(os.path.dirname(os.path.abspath(log_file)), exist_ok=True)
self.file_handler = open(log_file, 'w', encoding='utf-8')
# 进度跟踪
self.current_phase = ""
self.total_items = 0
self.current_item = 0
def _colorize(self, text: str, color: str) -> str:
if self.use_colors:
return f"{color}{text}{Colors.RESET}"
return text
def _format_time(self) -> str:
return datetime.now().strftime("%H:%M:%S")
def _write(self, message: str, level: str = ""):
# 输出到控制台
print(message, file=sys.stdout)
sys.stdout.flush()
# 输出到文件
if self.file_handler:
# 文件输出不带颜色
clean_msg = message
for color in vars(Colors).values():
if isinstance(color, str) and color.startswith('\033'):
clean_msg = clean_msg.replace(color, '')
timestamp = self._format_time()
self.file_handler.write(f"[{timestamp}] {level} {clean_msg}\n")
self.file_handler.flush()
def set_phase(self, phase: str, total: int = 0) -> None:
self.current_phase = phase
self.total_items = total
self.current_item = 0
self.progress(phase, 0, total)
def progress(self, message: str = "", current: int = 0, total: int = 0) -> None:
if total > 0:
self.total_items = total
if current > 0:
self.current_item = current
if self.total_items > 0:
percent: float = (self.current_item / self.total_items) * 100
bar_length: int = 30
filled: int = int(bar_length * self.current_item / self.total_items)
bar: str = '' * filled + '' * (bar_length - filled)
status: str = f"[{self._colorize(bar, Colors.BLUE)}] {self.current_item}/{self.total_items} ({percent:.1f}%)"
else:
status = ""
msg: str = f"{self._colorize('', Colors.BRIGHT_CYAN)} {self.current_phase}"
if status:
msg += f" {status}"
if message:
msg += f" - {message}"
# 使用 \r 实现进度更新
sys.stdout.write('\r' + msg)
sys.stdout.flush()
def debug(self, message: str, category: str = "") -> None:
if self.level > LogLevel.DEBUG:
return
prefix: str = self._colorize("DEBUG", Colors.GRAY)
if category:
prefix += f"[{self._colorize(category, Colors.CYAN)}]"
msg: str = f"{self._colorize('', Colors.GRAY)} {prefix}: {message}"
self._write(msg, "DEBUG")
def info(self, message: str, category: str = "") -> None:
prefix: str = self._colorize("INFO", Colors.GREEN)
if category:
prefix += f"[{self._colorize(category, Colors.CYAN)}]"
msg: str = f"{self._colorize('', Colors.GREEN)} {prefix}: {message}"
self._write(msg, "INFO")
def warning(self, message: str, category: str = "", exc_info: Exception | None = None) -> None:
prefix: str = self._colorize("WARN", Colors.BRIGHT_YELLOW)
if category:
prefix += f"[{self._colorize(category, Colors.CYAN)}]"
msg: str = f"{self._colorize('', Colors.YELLOW)} {prefix}: {message}"
self._write(msg, "WARNING")
def error(self, message: str, category: str = "", exception: Optional[Exception] = None) -> None:
prefix: str = self._colorize("ERROR", Colors.BRIGHT_RED)
if category:
prefix += f"[{self._colorize(category, Colors.CYAN)}]"
msg: str = f"{self._colorize('', Colors.RED)} {prefix}: {message}"
if exception:
msg += f"\n{self._colorize('', Colors.RED)} Exception: {type(exception).__name__}: {exception}"
self._write(msg, "ERROR")
def critical(self, message: str, category: str = "") -> None:
prefix: str = self._colorize("CRITICAL", Colors.BG_RED + Colors.WHITE + Colors.BOLD)
if category:
prefix += f"[{self._colorize(category, Colors.CYAN)}]"
msg: str = f"{self._colorize('', Colors.RED)} {prefix}: {message}"
self._write(msg, "CRITICAL")
def success(self, message: str, category: str = "") -> None:
prefix: str = self._colorize("SUCCESS", Colors.BRIGHT_GREEN)
if category:
prefix += f"[{self._colorize(category, Colors.CYAN)}]"
msg: str = f"{self._colorize('', Colors.GREEN)} {prefix}: {message}"
self._write(msg, "SUCCESS")
def compile_error(self, message: str, file: str = "", line: int = 0, code: str = "") -> None:
"""编译错误格式化输出"""
header: str = self._colorize("编译错误", Colors.BG_RED + Colors.WHITE + Colors.BOLD)
msg: str = f"\n{self._colorize('', Colors.RED)}{header}{self._colorize('', Colors.RED)}\n"
if file and line > 0:
location: str = f"{self._colorize(file, Colors.CYAN)}:{self._colorize(str(line), Colors.YELLOW)}"
msg += f"{self._colorize('', Colors.RED)} 位置:{location}\n"
if code:
msg += f"{self._colorize('', Colors.RED)} 代码:{self._colorize(code, Colors.WHITE)}\n"
msg += f"{self._colorize('', Colors.RED)} 错误:{self._colorize(message, Colors.BRIGHT_RED)}\n"
msg += f"{self._colorize('', Colors.RED)}{self._colorize('' * 50, Colors.RED)}{self._colorize('', Colors.RED)}"
self._write(msg, "ERROR")
def compile_warning(self, message: str, file: str = "", line: int = 0, code: str = "") -> None:
"""编译警告格式化输出"""
header: str = self._colorize("警告", Colors.BG_YELLOW + Colors.BLACK)
msg: str = f"\n{self._colorize('', Colors.YELLOW)}{header}{self._colorize('', Colors.YELLOW)}\n"
if file and line > 0:
location: str = f"{self._colorize(file, Colors.CYAN)}:{self._colorize(str(line), Colors.YELLOW)}"
msg += f"{self._colorize('', Colors.YELLOW)} 位置:{location}\n"
if code:
msg += f"{self._colorize('', Colors.YELLOW)} 代码:{self._colorize(code, Colors.WHITE)}\n"
msg += f"{self._colorize('', Colors.YELLOW)} 警告:{self._colorize(message, Colors.BRIGHT_YELLOW)}\n"
msg += f"{self._colorize('', Colors.YELLOW)}{self._colorize('' * 50, Colors.YELLOW)}{self._colorize('', Colors.YELLOW)}"
self._write(msg, "WARNING")
def summary(self, stats: dict[str, int | str]) -> None:
"""输出编译摘要"""
header: str = self._colorize("编译摘要", Colors.BG_BLUE + Colors.WHITE + Colors.BOLD)
msg: str = f"\n{self._colorize('', Colors.BLUE)}{header}{self._colorize('', Colors.BLUE)}\n"
for key, value in stats.items():
color: str = Colors.GREEN if value > 0 else Colors.WHITE
msg += f"{self._colorize('', Colors.BLUE)} {key}: {self._colorize(str(value), color)}\n"
msg += f"{self._colorize('', Colors.BLUE)}{self._colorize('' * 50, Colors.BLUE)}{self._colorize('', Colors.BLUE)}"
self._write(msg, "SUMMARY")
def close(self) -> None:
if self.file_handler:
self.file_handler.close()
self.info(f"日志已保存到:{self.log_file}")
# 全局日志实例
_global_logger: Optional[Logger] = None
def get_logger(name: str = "TransPyC", log_file: Optional[str] = None,
level: int = LogLevel.INFO, use_colors: bool = True) -> Logger:
global _global_logger
if _global_logger is None:
_global_logger = Logger(name, log_file, level, use_colors)
return _global_logger
def set_logger(logger: Logger) -> None:
global _global_logger
_global_logger = logger
# 便捷函数
def debug(msg: str, category: str = "") -> None:
get_logger().debug(msg, category)
def info(msg: str, category: str = "") -> None:
get_logger().info(msg, category)
def warning(msg: str, category: str = "") -> None:
get_logger().warning(msg, category)
def error(msg: str, category: str = "", exception: Exception | None = None) -> None:
get_logger().error(msg, category, exception)
def success(msg: str, category: str = "") -> None:
get_logger().success(msg, category)
def safe_call(func: Callable[..., Any], *args: Any, context: str = "", **kwargs: Any) -> Any:
"""安全调用函数,在异常时根据模式处理。
- strict 模式:重新抛出异常
- relaxed 模式:打印彩色警告日志并返回 None
Args:
func: 要调用的可调用对象
*args, **kwargs: 传给 func 的参数
context: 描述性上下文(用于日志消息)
Returns:
func 的返回值,或 None异常时
"""
try:
return func(*args, **kwargs)
except Exception as e:
msg = f"{context}: {type(e).__name__}: {e}" if context else f"{type(e).__name__}: {e}"
if _ConfigMode == "strict":
raise
get_logger().warning(msg, "Exception")
return None