diff --git a/config.ini b/config.ini index 718d988..609f74c 100644 --- a/config.ini +++ b/config.ini @@ -18,12 +18,23 @@ Upper_Port = 8088 Decoder_Host = 127.0.0.1 Decoder_Port = 8099 Serial_port = COM44 -algo_log_path = d:/Program Files/64chn_Decoder/logs -algo_log_level = DEBUG -console_output = 1 save_train_data = 0 zmqServer_host = 127.0.0.1 +[algo_log] +# ========== 文件日志配置 ========== +file_log_enable = true +file_log_level = DEBUG +log_path = exe +retention_days = 3 + +# ========== 控制台/黑框配置 ========== +console_enable = true +console_show_window = true +console_log_level = DEBUG + + + ; 64 导设备配置 [device_type_1] sample_rate = 250 diff --git a/logs/log.py b/logs/log.py index 1c8a810..6265785 100644 --- a/logs/log.py +++ b/logs/log.py @@ -1,122 +1,172 @@ import os +import sys from pathlib import Path from datetime import datetime, timedelta import logging from logging.handlers import RotatingFileHandler import inspect +try: + import win32gui + import win32con + WIN32_AVAILABLE = True +except ImportError: + WIN32_AVAILABLE = False + from PubLibrary.InifileHelper import IniRead +# ===================== 新增:获取 EXE 同级目录 ===================== +def get_app_root(): + """获取 runDecoder.exe 所在的真实根目录(兼容 onefile / standalone)""" + if getattr(sys, 'frozen', False): + # Nuitka / PyInstaller 打包后走这里 + app_path = sys.executable + else: + # 本地源码运行时,取当前脚本目录 + app_path = os.path.abspath(__file__) + return os.path.dirname(app_path) -# 全局配置 -console_output = IniRead('system', 'console_output', '1') -log_level = IniRead('system', 'algo_log_level', 'INFO') +# 程序根目录(exe 同级) +APP_ROOT = Path(get_app_root()) +# 日志文件夹名:exe 同级下 logs 目录 +DEFAULT_LOG_DIR = APP_ROOT / "logs" + +# ===================== 读取 [algo_log] 配置 ===================== +# 文件日志 +FILE_LOG_ENABLE = IniRead("algo_log", "file_log_enable", "true").lower() == "true" +FILE_LOG_LEVEL = IniRead("algo_log", "file_log_level", "DEBUG").upper() +# 优先级:配置文件 > 默认exe同级logs +CFG_LOG_PATH = IniRead("algo_log", "log_path", "").strip() +if CFG_LOG_PATH == "exe": + LOG_DIR = DEFAULT_LOG_DIR +else: + LOG_DIR = Path(CFG_LOG_PATH) + +LOG_RETENTION_DAYS = int(IniRead("algo_log", "retention_days", 3)) + +# 控制台日志 + 黑框控制 +CONSOLE_ENABLE = IniRead("algo_log", "console_enable", "true").lower() == "true" +CONSOLE_SHOW_WINDOW = IniRead("algo_log", "console_show_window", "true").lower() == "true" +CONSOLE_LOG_LEVEL = IniRead("algo_log", "console_log_level", "INFO").upper() + +# ===================== 全局常量与缓存 ===================== log_once_cache = set() logger_cache = {} -LOG_RETENTION_DAYS = 3 - -LOG_PATH_STR = IniRead('system', 'algo_log_path', "d:/Program Files/64chn_Decoder/logs") -LOG_DIR = Path(LOG_PATH_STR) -# 自动补全路径分隔符,创建目录(不存在则新建,避免写日志报错) -LOG_DIR.mkdir(parents=True, exist_ok=True) -# 如需字符串格式路径 -LOG_DIR_STR = str(LOG_DIR) + "\\" LOG_FILE_PREFIX = 'algo_log_' +# 确保日志目录存在 +LOG_DIR.mkdir(parents=True, exist_ok=True) +LOG_DIR_STR = str(LOG_DIR) + "\\" -# 日志格式:时间 - 日志器名 - 级别 - 文件名:行号 - 函数名 - 日志内容 +# 日志格式 LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' DATE_FORMAT = '%Y-%m-%d %H:%M:%S' +# 日志级别映射 +LEVEL_MAP = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "FATAL": logging.FATAL +} +FILE_LOG_LEVEL_INT = LEVEL_MAP.get(FILE_LOG_LEVEL, logging.INFO) +CONSOLE_LOG_LEVEL_INT = LEVEL_MAP.get(CONSOLE_LOG_LEVEL, logging.INFO) -def clean_old_logs(): - """清理超过指定天数的旧日志文件""" +# ===================== Windows 控制台黑框显示/隐藏 ===================== +def control_console_window(): + if not sys.platform.startswith("win") or not WIN32_AVAILABLE: + return try: - if not os.path.exists(LOG_DIR): + hwnd = win32gui.GetForegroundWindow() + if CONSOLE_SHOW_WINDOW: + win32gui.ShowWindow(hwnd, win32con.SW_SHOW) + else: + win32gui.ShowWindow(hwnd, win32con.SW_HIDE) + except Exception: + pass + +control_console_window() + +# ===================== 清理过期日志 ===================== +def clean_old_logs(): + try: + if not LOG_DIR.exists(): return expire_date = datetime.now() - timedelta(days=LOG_RETENTION_DAYS) for filename in os.listdir(LOG_DIR): - if not filename.startswith(LOG_FILE_PREFIX) or not filename.endswith('.log'): + if not (filename.startswith(LOG_FILE_PREFIX) and filename.endswith('.log')): continue date_str = filename[len(LOG_FILE_PREFIX):-4] try: file_date = datetime.strptime(date_str, '%Y-%m-%d') if file_date < expire_date: - file_path = os.path.join(LOG_DIR, filename) + file_path = LOG_DIR / filename os.remove(file_path) - print(f"清理过期日志: {file_path}") except ValueError: continue - except Exception as e: - print(f"清理旧日志异常: {str(e)}") - + except Exception: + pass +# ===================== 初始化日志器 ===================== def init_module_logger(logger_name): - """初始化日志器 + 清理旧日志""" - os.makedirs(LOG_DIR, exist_ok=True) - clean_old_logs() - - current_date = datetime.now().strftime("%Y-%m-%d") - log_file = os.path.join(LOG_DIR, f"{LOG_FILE_PREFIX}{current_date}.log") - if logger_name in logger_cache: return logger_cache[logger_name] + clean_old_logs() + logger = logging.getLogger(logger_name) - logger.setLevel(log_level) + logger.setLevel(logging.DEBUG) if logger.handlers: logger_cache[logger_name] = logger return logger - # 文件输出处理器 - file_handler = RotatingFileHandler( - log_file, - maxBytes=10 * 1024 * 1024, - backupCount=10, - encoding='utf-8' - ) formatter = logging.Formatter(LOG_FORMAT, datefmt=DATE_FORMAT) - file_handler.setFormatter(formatter) - logger.addHandler(file_handler) - # 控制台输出 - if console_output: - console_handler = logging.StreamHandler() + # 文件日志 + if FILE_LOG_ENABLE: + current_date = datetime.now().strftime("%Y-%m-%d") + log_file = LOG_DIR / f"{LOG_FILE_PREFIX}{current_date}.log" + file_handler = RotatingFileHandler( + log_file, + maxBytes=10 * 1024 * 1024, + backupCount=10, + encoding='utf-8' + ) + file_handler.setFormatter(formatter) + file_handler.setLevel(FILE_LOG_LEVEL_INT) + logger.addHandler(file_handler) + + # 控制台日志 + if CONSOLE_ENABLE: + console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(formatter) + console_handler.setLevel(CONSOLE_LOG_LEVEL_INT) logger.addHandler(console_handler) logger_cache[logger_name] = logger return logger - +# ===================== 对外日志入口函数 ===================== def algo_log(content, level="INFO", record_once=False): - """ - 日志入口函数 - 自动记录:调用文件名、代码行号、所在函数 - """ - # 回溯栈帧,获取真正调用 algo_log 的代码位置 - # f_back(1) -> algo_log 自身,f_back(2) -> 业务调用处 - frame = inspect.currentframe().f_back.f_back - if not frame: - file_name = "unknown" - else: - file_name = os.path.basename(frame.f_code.co_filename) + frame = inspect.currentframe() + if frame: + frame = frame.f_back.f_back + file_name = os.path.basename(frame.f_code.co_filename) if frame else "unknown" logger = init_module_logger(file_name) - # 单次日志去重 if record_once: log_key = f"{level.upper()}_{content}" if log_key in log_once_cache: return log_once_cache.add(log_key) - # 日志级别分发 level_upper = level.upper() - log_map = { + log_func_map = { "DEBUG": logger.debug, + "INFO": logger.info, "WARNING": logger.warning, "ERROR": logger.error, - "FATAL": logger.fatal, - "INFO": logger.info + "FATAL": logger.fatal } - log_func = log_map.get(level_upper, logger.info) + log_func = log_func_map.get(level_upper, logger.info) log_func(content) \ No newline at end of file diff --git a/nuitka_3in1_package.sh b/nuitka_3in1_package.sh index 35d9577..c799884 100644 --- a/nuitka_3in1_package.sh +++ b/nuitka_3in1_package.sh @@ -28,7 +28,6 @@ echo "输出目录:${OUT_DIR}" python -m nuitka \ --standalone \ --msvc=latest \ ---windows-console-mode=force \ --module-parameter=torch-disable-jit=yes \ --enable-plugin=no-qt \ --include-package=numpy \