问题描述
在使用 Hydra 框架开发过程中,为了自定义日志的时间格式和日志文件编码规则, 通过logging.basicConfig方法进行配置,但实际运行后发现这些配置均未生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import logging import hydra from omegaconf import DictConfig
logging.basicConfig( level=logging.INFO, format='[%(asctime)s][%(name)s][%(levelname)s] - %(message)s', datefmt="%H:%M:%S", encoding="utf-8" ) log = logging.getLogger() log.info("Test")
@hydra.main(version_base=None, config_path="configs", config_name="config") def main(cfg: DictConfig): log.info(f"{cfg.task.name}") log.info(f"{cfg.task.name}")
if __name__ == "__main__": main()
|
运行结果如下:
1 2 3
| [06:52:53][root][INFO] - Test [2026-01-14 06:52:53,475][root][INFO] - Test [2026-01-14 06:52:53,475][root][INFO] - Test
|
问题解析
Hydra默认的日志配置定义在hydra/conf/hydra/job_logging/default.yaml中,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| version: 1 formatters: simple: format: '[%(asctime)s][%(name)s][%(levelname)s] - %(message)s' handlers: console: class: logging.StreamHandler formatter: simple stream: ext://sys.stdout file: class: logging.FileHandler formatter: simple filename: ${hydra.runtime.output_dir}/${hydra.job.name}.log root: level: INFO handlers: [ console, file ]
disable_existing_loggers: false
|
Hydra内部会通过hydra/core/utils.py文件中的configure_log函数,调用logging.config.dictConfig(conf)重新配置root logger。
根据Python logging模块的特性,dictConfig会清空root logger原有的所有Handler并重建配置,因此提前通过basicConfig
设置的日志规则会被完全覆盖。
解决方案
方案1:自定义yaml文件完全覆盖默认配置
根据Hydra官方文档说明,可通过自定义yaml配置文件完全替换默认的日志规则,操作步骤如下:
- 在项目目录下新建
hydra/job_logging/custom.yaml文件,参考默认配置编写自定义日志规则(如调整日志格式、修改输出级别等);
- 在项目主配置文件中,通过
defaults字段指定使用该自定义日志配置:
1 2
| defaults: - override hydra/job_logging: custom
|
方案2:主配置文件选择性覆盖配置项
若仅需修改默认配置中的个别参数(如日期格式、文件编码),可直接在主配置文件中针对性覆盖,示例配置如下:
1 2 3 4 5 6 7 8
| hydra: job_logging: formatters: simple: datefmt: '%H:%M:%S' handlers: file: encoding: utf-8
|
方案3:运行时动态修改Handler
可在@hydra.main装饰的函数内部,直接操作root logger的Handler对象,实现日志配置的动态调整。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import logging import hydra from omegaconf import DictConfig
log = logging.getLogger()
@hydra.main(version_base=None, config_path="configs", config_name="config") def main(cfg: DictConfig): log.info(f"{cfg.task.name}") root_logger = logging.getLogger() for handler in root_logger.handlers: current_fmt = handler.formatter._fmt formatter = logging.Formatter(current_fmt, datefmt="%H:%M:%S") handler.setFormatter(formatter) log.info(f"{cfg.task.name}")
if __name__ == "__main__": main()
|