diff --git a/src/heurams/__init__.py b/src/heurams/__init__.py index 6d08cb3..7d381b6 100644 --- a/src/heurams/__init__.py +++ b/src/heurams/__init__.py @@ -1 +1,6 @@ print("欢迎使用 HeurAMS 及其组件!") + +# 补充日志记录 +from heurams.services.logger import get_logger +logger = get_logger(__name__) +logger.info("欢迎使用 HeurAMS 及其组件!") diff --git a/src/heurams/context.py b/src/heurams/context.py index 98a6a30..86b007a 100644 --- a/src/heurams/context.py +++ b/src/heurams/context.py @@ -6,6 +6,7 @@ from contextvars import ContextVar import pathlib from heurams.services.config import ConfigFile +from heurams.services.logger import get_logger # 默认配置文件路径规定: 以包目录为准 # 用户配置文件路径规定: 以运行目录为准 @@ -13,8 +14,11 @@ from heurams.services.config import ConfigFile rootdir = pathlib.Path(__file__).parent print(f"rootdir: {rootdir}") +logger = get_logger(__name__) +logger.debug(f"项目根目录: {rootdir}") workdir = pathlib.Path.cwd() print(f"workdir: {workdir}") +logger.debug(f"工作目录: {workdir}") config_var: ContextVar[ConfigFile] = ContextVar( "config_var", default=ConfigFile(rootdir / "default" / "config" / "config.toml") ) @@ -23,8 +27,10 @@ try: "config_var", default=ConfigFile(workdir / "config" / "config.toml") ) # 配置文件 print("已加载自定义用户配置") -except: + logger.info("已加载自定义用户配置,路径: %s", workdir / "config" / "config.toml") +except Exception as e: print("未能加载自定义用户配置") + logger.warning("未能加载自定义用户配置,错误: %s", e) # runtime_var: ContextVar = ContextVar('runtime_var', default=dict()) # 运行时共享数据 diff --git a/src/heurams/kernel/reactor/fission.py b/src/heurams/kernel/reactor/fission.py index 2cb51bd..3aac045 100644 --- a/src/heurams/kernel/reactor/fission.py +++ b/src/heurams/kernel/reactor/fission.py @@ -2,12 +2,14 @@ import heurams.kernel.particles as pt import heurams.kernel.puzzles as puz import random from .states import PhaserState +from heurams.services.logger import get_logger class Fission: """裂变器: 单原子调度展开器""" def __init__(self, atom: pt.Atom, phase=PhaserState.RECOGNITION): + self.logger = get_logger(__name__) self.atom = atom #print(f"{phase.value}") self.orbital_schedule = atom.registry["orbital"]["schedule"][phase.value] # type: ignore @@ -16,6 +18,7 @@ class Fission: self.puzzles = list() for item, possibility in self.orbital_schedule: # type: ignore print(f"ad:{item}") + self.logger.debug(f"开始处理 orbital 项: {item}") if not isinstance(possibility, float): possibility = float(possibility) while possibility > 1: @@ -34,6 +37,7 @@ class Fission: } ) print(f"ok:{item}") + self.logger.debug(f"orbital 项处理完成: {item}") def generate(self): diff --git a/src/heurams/services/config.py b/src/heurams/services/config.py index cc4cf9d..84cf9d7 100644 --- a/src/heurams/services/config.py +++ b/src/heurams/services/config.py @@ -2,13 +2,16 @@ import pathlib import toml import typing +from heurams.services.logger import get_logger class ConfigFile: def __init__(self, path: pathlib.Path): + self.logger = get_logger(__name__) self.path = path if not self.path.exists(): self.path.touch() + self.logger.debug("创建配置文件: %s", self.path) self.data = dict() self._load() @@ -17,13 +20,16 @@ class ConfigFile: with open(self.path, "r") as f: try: self.data = toml.load(f) + self.logger.debug("配置文件加载成功: %s", self.path) except toml.TomlDecodeError as e: print(f"{e}") + self.logger.error("TOML解析错误: %s", e) self.data = {} def modify(self, key: str, value: typing.Any): """修改配置值并保存""" self.data[key] = value + self.logger.debug("修改配置项: %s = %s", key, value) self.save() def save(self, path: typing.Union[str, pathlib.Path] = ""): @@ -31,6 +37,7 @@ class ConfigFile: save_path = pathlib.Path(path) if path else self.path with open(save_path, "w") as f: toml.dump(self.data, f) + self.logger.debug("配置文件已保存: %s", save_path) def get(self, key: str, default: typing.Any = None) -> typing.Any: """获取配置值,如果不存在返回默认值""" diff --git a/src/heurams/services/logger.py b/src/heurams/services/logger.py new file mode 100644 index 0000000..567fe28 --- /dev/null +++ b/src/heurams/services/logger.py @@ -0,0 +1,155 @@ +""" +HeurAMS 日志服务模块 +基于Python标准logging库, 提供统一的日志记录功能 +""" + +import logging +import logging.handlers +import pathlib +import sys +from typing import Optional, Union + +# 默认配置 +DEFAULT_LOG_LEVEL = logging.DEBUG +DEFAULT_LOG_FILE = pathlib.Path("/tmp/heurams.log") +DEFAULT_LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" +DEFAULT_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" + +# 全局logger缓存 +_loggers = {} + + +def setup_logging( + log_file: Union[str, pathlib.Path] = DEFAULT_LOG_FILE, + log_level: int = DEFAULT_LOG_LEVEL, + log_format: str = DEFAULT_LOG_FORMAT, + date_format: str = DEFAULT_DATE_FORMAT, + max_bytes: int = 10 * 1024 * 1024, # 10MB + backup_count: int = 5, +) -> None: + """ + 配置全局日志系统 + + Args: + log_file: 日志文件路径 + log_level: 日志级别 (logging.DEBUG, logging.INFO等) + log_format: 日志格式字符串 + date_format: 日期时间格式 + max_bytes: 单个日志文件最大字节数 + backup_count: 备份文件数量 + """ + # 确保日志目录存在 + log_path = pathlib.Path(log_file) + log_path.parent.mkdir(parents=True, exist_ok=True) + + # 创建formatter + formatter = logging.Formatter(log_format, date_format) + + # 创建文件handler(使用RotatingFileHandler防止日志过大) + file_handler = logging.handlers.RotatingFileHandler( + filename=log_path, + maxBytes=max_bytes, + backupCount=backup_count, + encoding='utf-8' + ) + file_handler.setFormatter(formatter) + file_handler.setLevel(log_level) + + # 配置root logger - 设置为 WARNING 级别(只记录重要信息) + root_logger = logging.getLogger() + root_logger.setLevel(logging.WARNING) # 这里改为 WARNING + + # 移除所有现有handler + for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + + # 创建自己的应用logger(单独设置DEBUG级别) + app_logger = logging.getLogger("heurams") + app_logger.setLevel(log_level) # 保持DEBUG级别 + app_logger.addHandler(file_handler) + + # 禁止传播到root logger,避免双重记录 + app_logger.propagate = False + + # 设置第三方库的日志级别为WARNING,避免调试信息干扰 + third_party_loggers = [ + "markdown_it", + "markdown_it.rules_block", + "markdown_it.rules_core", + "markdown_it.rules_inline", + "asyncio", + ] + + for logger_name in third_party_loggers: + logging.getLogger(logger_name).setLevel(logging.WARNING) + + # 记录日志系统初始化 + app_logger.info("日志系统已初始化, 日志文件: %s", log_path) + + +def get_logger(name: Optional[str] = None) -> logging.Logger: + """ + 获取指定名称的logger + + Args: + name: logger名称, 通常使用模块名(__name__) + 如果为None, 返回root logger + + Returns: + logging.Logger实例 + """ + if name is None: + return logging.getLogger() + + # 确保使用 heurams 作为前缀,继承应用logger的配置 + if not name.startswith("heurams") and name != "": + logger_name = f"heurams.{name}" + else: + logger_name = name + + # 缓存logger以提高性能 + if logger_name not in _loggers: + logger = logging.getLogger(logger_name) + _loggers[logger_name] = logger + + return _loggers[logger_name] + + +# 便捷函数 +def debug(msg: str, *args, **kwargs) -> None: + """DEBUG级别日志""" + get_logger().debug(msg, *args, **kwargs) + + +def info(msg: str, *args, **kwargs) -> None: + """INFO级别日志""" + get_logger().info(msg, *args, **kwargs) + + +def warning(msg: str, *args, **kwargs) -> None: + """WARNING级别日志""" + get_logger().warning(msg, *args, **kwargs) + + +def error(msg: str, *args, **kwargs) -> None: + """ERROR级别日志""" + get_logger().error(msg, *args, **kwargs) + + +def critical(msg: str, *args, **kwargs) -> None: + """CRITICAL级别日志""" + get_logger().critical(msg, *args, **kwargs) + + +def exception(msg: str, *args, **kwargs) -> None: + """记录异常信息 (ERROR级别) """ + get_logger().exception(msg, *args, **kwargs) + + +# 初始化日志系统(硬编码配置) +setup_logging() + + +# 模块级别的logger实例 +logger = get_logger(__name__) +logger.info("HeurAMS日志服务模块已加载") \ No newline at end of file