295 lines
10 KiB
Python
295 lines
10 KiB
Python
import t, c
|
||
from stdint import *
|
||
import stdio
|
||
import stdlib
|
||
import memhub
|
||
import sys
|
||
import w32.win32console as w32cmd
|
||
import w32.win32file as w32file
|
||
import w32.win32base as win32base
|
||
|
||
|
||
# ============================================================
|
||
# VLogger - 原生日志系统(Windows 控制台彩色输出)
|
||
#
|
||
# 输出格式与 TPC (lib/core/VLogger.py) 对齐:
|
||
# ├ INFO: message
|
||
# ├ INFO[category]: message
|
||
# ├ WARN: message
|
||
# ├ ERROR: message
|
||
# ├ SUCCESS: message
|
||
# ├ DEBUG: message
|
||
# ╠ CRITICAL: message (红底白字,致命错误)
|
||
# ╠ CRITICAL[category]: message
|
||
#
|
||
# 分段着色:前缀符号(├/╠) 用级别色,LEVEL 标签用亮色,消息用默认色
|
||
# CRITICAL 级别使用 ╠ 前缀和红底白字标签,与 TPC critical 对齐
|
||
# ============================================================
|
||
|
||
# 日志级别
|
||
class LogLevel(t.CEnum):
|
||
DEBUG = 0
|
||
INFO = 1
|
||
WARNING = 2
|
||
ERROR = 3
|
||
SUCCESS = 4
|
||
CRITICAL = 5
|
||
|
||
|
||
# Win32 控制台前景色属性
|
||
FOREGROUND_BLUE: t.CDefine = 0x0001
|
||
FOREGROUND_GREEN: t.CDefine = 0x0002
|
||
FOREGROUND_RED: t.CDefine = 0x0004
|
||
FOREGROUND_INTENSITY: t.CDefine = 0x0008
|
||
FOREGROUND_WHITE: t.CDefine = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||
FOREGROUND_CYAN: t.CDefine = FOREGROUND_BLUE | FOREGROUND_GREEN
|
||
FOREGROUND_YELLOW: t.CDefine = FOREGROUND_RED | FOREGROUND_GREEN
|
||
FOREGROUND_MAGENTA: t.CDefine = FOREGROUND_RED | FOREGROUND_BLUE
|
||
|
||
# Win32 控制台背景色属性
|
||
BACKGROUND_RED: t.CDefine = 0x0040
|
||
BACKGROUND_GREEN: t.CDefine = 0x0020
|
||
BACKGROUND_BLUE: t.CDefine = 0x0010
|
||
BACKGROUND_INTENSITY: t.CDefine = 0x0080
|
||
BACKGROUND_WHITE: t.CDefine = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||
|
||
|
||
class Logger:
|
||
_level: int
|
||
_console_handle: w32base.HANDLE
|
||
_use_color: int
|
||
__mbuddy__: memhub.MemManager | t.CPtr
|
||
|
||
def __init__(self, level: int = 1):
|
||
self._level = level
|
||
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
|
||
self._use_color = 1
|
||
self.__mbuddy__ = _mbuddy
|
||
|
||
def _set_color(self, attr: WORD) -> int:
|
||
if self._use_color:
|
||
return w32cmd.SetConsoleTextAttribute(self._console_handle, attr)
|
||
return 0
|
||
|
||
def _reset_color(self) -> int:
|
||
if self._use_color:
|
||
return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# _log - 核心日志输出(分段着色)
|
||
#
|
||
# 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg
|
||
# 分三段着色:
|
||
# 1. 前缀符号 ├ (sym_color)
|
||
# 2. LEVEL 标签 (level_color)
|
||
# 3. 消息内容 (默认白色)
|
||
# ============================================================
|
||
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str,
|
||
sym_color: WORD, level_color: WORD,
|
||
category: str = "") -> int:
|
||
if level < self._level:
|
||
return 0
|
||
# 前缀符号(如 ├)
|
||
self._set_color(sym_color)
|
||
stdio.printf("%s ", prefix_sym)
|
||
# LEVEL 标签(亮色)
|
||
self._set_color(level_color)
|
||
if category is not None:
|
||
if category[0] != 0:
|
||
stdio.printf("%s[%s]: ", level_str, category)
|
||
else:
|
||
stdio.printf("%s: ", level_str)
|
||
else:
|
||
stdio.printf("%s: ", level_str)
|
||
# 消息内容(默认色)
|
||
self._reset_color()
|
||
stdio.printf("%s\n", msg)
|
||
return 0
|
||
|
||
def debug(self, msg: str, category: str = "") -> int:
|
||
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg,
|
||
FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
|
||
|
||
def info(self, msg: str, category: str = "") -> int:
|
||
return self._log(LogLevel.INFO, "├", "INFO", msg,
|
||
FOREGROUND_GREEN,
|
||
FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||
|
||
def warning(self, msg: str, category: str = "") -> int:
|
||
return self._log(LogLevel.WARNING, "├", "WARN", msg,
|
||
FOREGROUND_YELLOW,
|
||
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
|
||
category)
|
||
|
||
def error(self, msg: str, category: str = "") -> int:
|
||
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg,
|
||
FOREGROUND_RED,
|
||
FOREGROUND_RED | FOREGROUND_INTENSITY, category)
|
||
# 错误立即终止进程(避免错误后继续执行导致连锁崩溃)
|
||
sys.exit(1)
|
||
return ret
|
||
|
||
def success(self, msg: str, category: str = "") -> int:
|
||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg,
|
||
FOREGROUND_GREEN,
|
||
FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||
|
||
def critical(self, msg: str, category: str = "") -> int:
|
||
# CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格)
|
||
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg,
|
||
FOREGROUND_RED,
|
||
BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY,
|
||
category)
|
||
|
||
def set_level(self, level: int) -> int:
|
||
self._level = level
|
||
return 0
|
||
|
||
# ============================================================
|
||
# 高级日志方法
|
||
# ============================================================
|
||
|
||
def banner(self, msg: str) -> int:
|
||
"""输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。"""
|
||
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
|
||
stdio.printf("\n=== %s ===\n\n", msg)
|
||
self._reset_color()
|
||
return 0
|
||
|
||
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
|
||
"""格式化编译错误输出(红底白字标题 + 位置 + 错误信息)。"""
|
||
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
|
||
stdio.printf(" 编译错误 ")
|
||
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
|
||
stdio.printf("\n")
|
||
if file is not None:
|
||
if file[0] != 0 and line > 0:
|
||
stdio.printf(" 位置: %s:%d\n", file, line)
|
||
elif file[0] != 0:
|
||
stdio.printf(" 文件: %s\n", file)
|
||
stdio.printf(" 错误: %s\n", msg)
|
||
self._reset_color()
|
||
return 0
|
||
|
||
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
|
||
"""格式化编译警告输出(黄底黑字标题 + 位置 + 警告信息)。"""
|
||
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
|
||
stdio.printf(" 编译警告 ")
|
||
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
|
||
stdio.printf("\n")
|
||
if file is not None:
|
||
if file[0] != 0 and line > 0:
|
||
stdio.printf(" 位置: %s:%d\n", file, line)
|
||
elif file[0] != 0:
|
||
stdio.printf(" 文件: %s\n", file)
|
||
stdio.printf(" 警告: %s\n", msg)
|
||
self._reset_color()
|
||
return 0
|
||
|
||
|
||
# 全局 mbuddy 指针(由 lib.InitLib 注入)
|
||
_mbuddy: memhub.MemManager | t.CPtr
|
||
|
||
# 全局 logger 指针
|
||
_g_logger: Logger | t.CPtr
|
||
|
||
# 全局格式化缓冲区(用于带变量的日志消息,1024 字节)
|
||
_fmt_buf: t.CChar | t.CPtr
|
||
|
||
|
||
def fmt_buf() -> t.CChar | t.CPtr:
|
||
"""获取全局格式化缓冲区(1024 字节)。
|
||
|
||
用法:
|
||
buf: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||
if buf is not None:
|
||
viperlib.snprintf(buf, 1024, "翻译: %s (sha1=%s)", rp, sha1)
|
||
log.info(buf, "Phase1")
|
||
"""
|
||
global _fmt_buf
|
||
if _fmt_buf is None:
|
||
_fmt_buf = stdlib.malloc(1024)
|
||
return _fmt_buf
|
||
|
||
|
||
def get_logger() -> Logger | t.CPtr:
|
||
"""获取全局 logger 实例。若不存在则通过 _mbuddy 分配并初始化。"""
|
||
global _g_logger
|
||
if _g_logger is None:
|
||
if _mbuddy is None:
|
||
return None
|
||
raw: t.CVoid | t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
|
||
if raw is None:
|
||
return None
|
||
_g_logger = raw
|
||
_g_logger._level = LogLevel.INFO
|
||
_g_logger._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
|
||
_g_logger._use_color = 1
|
||
_g_logger.__mbuddy__ = _mbuddy
|
||
return _g_logger
|
||
|
||
|
||
def set_logger(logger: Logger | t.CPtr) -> int:
|
||
"""设置全局 logger 实例。"""
|
||
global _g_logger
|
||
_g_logger = logger
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐)
|
||
#
|
||
# 调用方式: VLogger.info("msg", "category")
|
||
# 无需先 get_logger(),方便各模块直接使用
|
||
# ============================================================
|
||
|
||
def info(msg: str, category: str = "") -> int:
|
||
"""输出 INFO 级别日志。"""
|
||
log: Logger | t.CPtr = get_logger()
|
||
if log is not None:
|
||
return log.info(msg, category)
|
||
return 0
|
||
|
||
|
||
def warning(msg: str, category: str = "") -> int:
|
||
"""输出 WARN 级别日志。"""
|
||
log: Logger | t.CPtr = get_logger()
|
||
if log is not None:
|
||
return log.warning(msg, category)
|
||
return 0
|
||
|
||
|
||
def error(msg: str, category: str = "") -> int:
|
||
"""输出 ERROR 级别日志并立即终止进程。"""
|
||
log: Logger | t.CPtr = get_logger()
|
||
if log is not None:
|
||
return log.error(msg, category)
|
||
# logger 不可用时也直接退出
|
||
sys.exit(1)
|
||
return 0
|
||
|
||
|
||
def success(msg: str, category: str = "") -> int:
|
||
"""输出 SUCCESS 级别日志。"""
|
||
log: Logger | t.CPtr = get_logger()
|
||
if log is not None:
|
||
return log.success(msg, category)
|
||
return 0
|
||
|
||
|
||
def critical(msg: str, category: str = "") -> int:
|
||
"""输出 CRITICAL 级别日志(致命错误,红底白字)。"""
|
||
log: Logger | t.CPtr = get_logger()
|
||
if log is not None:
|
||
return log.critical(msg, category)
|
||
return 0
|
||
|
||
|
||
def debug(msg: str, category: str = "") -> int:
|
||
"""输出 DEBUG 级别日志。"""
|
||
log: Logger | t.CPtr = get_logger()
|
||
if log is not None:
|
||
return log.debug(msg, category)
|
||
return 0
|