Skip to content

重构日志子系统:修复失效的 bind(no_format)、复用 normalize_config 消除重复、停止改写根 logger #160

Description

@SongshGeo

现象

日志功能分散在 5 个模块里,是全包复杂度最集中的地方。分层本身(log_parserlog_config → {logging, exp_logging})是合理的,问题全在叶子上:重复实现、loguru 迁移的残留、以及一个已经失效但没人发现的格式化开关。

复杂度排行榜前几名有一半在这里:

函数 行数 分支
setup_exp_logger 147 27
setup_model_logger 100 8
MainModel._setup_logger 88 19
setup_integrated_logging 87 6

逐条

1. LoggerAdapter.bind(no_format=True) 完全无效

abses/utils/log_config.py

def _format_message(self, message: str) -> str:
    if self._extra.get("no_format"):
        return message
    return message          # ← 两个分支返回同一个东西

abses/utils/logging.py 里四处横幅输出(log_sessionsetup_logger_info ×2、log_repeat_separator)都调用 logger.bind(no_format=True),本意是让 ASCII 横幅不带时间戳前缀。实际每一行都被打上 [12:00:00][abses][INFO] - 。这是 loguru → 标准库迁移遗留的未修回归,日志文件里能直接看到。

同源残留:LoggerAdapter.add() 永远返回 0 什么都不做(用户以为加了 handler,其实没有),remove(None) 会清掉全部 handler。

要么把 no_format 真正实现(例如切到一个 SIMPLE_FORMAT 的 handler,或用 extra + 自定义 Formatter),要么把这个 API 连同 LoggerAdapter 一起删掉、直接用标准 logging.Logger

2. exp_logging.py 有大段重复,而且重造了已有的轮子

「入参是 dict 还是 DictConfig」这个分支在同一个函数里逐字重复了 4 次outpath 解析块逐字重复了 2 次:

if isinstance(cfg, dict):
    outpath = cfg.get("outpath")
else:
    try:
        outpath = OmegaConf.select(cfg, "outpath", default=None)
    except Exception:
        outpath = None

两个问题:

  • abses/utils/config.py 已经有 normalize_config() 专门做 dict/DictConfig/None → DictConfig 的归一化,这里没复用。
  • 那四个 except Exception 包的是 OmegaConf.select(..., default=...),而它对缺失键根本不抛异常。这四个兜底唯一的作用是掩盖真实的插值错误 / ConfigAttributeError

抽出一个 _resolve_outpath(cfg) 和一个 _select(cfg, key, default),这个文件能缩掉近一半。

3. 死代码

  • abses/utils/hydra_logging.py —— generate_hydra_job_logging 全仓唯一命中就是它自己的定义,0% 覆盖率。已在 71a5b34 删除。
  • configure_root_loggerlogging.formatter / FORMAT(后者还是 loguru 的 {time:HH:mm:ss} 花括号语法却挂在 __all__ 里)—— 零调用点。已在 71a5b34 删除。

4. 格式常量三处重复定义

"[%(asctime)s][%(name)s][%(levelname)s] - %(message)s" / "%H:%M:%S" / "INFO"log_config.pylog_parser.py、(已删的)hydra_logging.py 各定义一遍。log_config.py 的注释自己都写着 # Default values (matching log_parser.py)。收敛到一个模块。

5. 作为库,破坏性地重配了根 logger

abses/utils/logging.pysetup_model_logger

for handler in root_logger.handlers[:]:
    handler_name = getattr(handler, "name", "") or ""
    if "hydra" not in handler_name.lower():
        root_logger.removeHandler(handler)

handler.name子串嗅探来摘除根 logger 上的第三方 handler,再把 ABSESpy 自己的挂上去。叠加 setup_abses_logger 无条件 logger.handlers.clear()setup_exp_logger 清掉 abses.core 父 logger,整体顺序敏感且无法单独测试。

一个库不应该改根 logger 的配置——那是应用(或 Hydra)的职责。

6. 覆盖率

exp_logging.py 58%,是全包最低的几个之一。

验收标准

  • 横幅日志在文件和终端里都不带时间戳前缀(或者 no_format 这个概念被彻底移除)。
  • exp_logging.py 里不再有 dict/DictConfig 的重复分支,改为复用 normalize_config()
  • 不再有包住 OmegaConf.select(..., default=...)except Exception
  • 格式常量只有一处定义。
  • 库不再无条件清空根 logger 的 handler(或给出显式开关,默认关)。
  • exp_logging.py 覆盖率提到 80% 以上。
  • 改动到的文件里,注释与 docstring 统一为英文。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ♻️ refactorCode restructuring without behaviour change

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions