You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Python模块日志器适配任意主脚本日志器?

让Python模块日志自动继承主脚本日志器层级的解决方案

我明白你的需求:希望模块被任意脚本导入后,模块的日志器能自动挂靠在主脚本创建的日志器之下,不管主日志器是root还是自定义命名的(比如main)。这个问题的核心在于默认情况下,模块用__name__创建的日志器(比如module)父级是root,不会自动关联到主脚本的自定义日志器。

下面是具体的实现方案:

核心思路

通过动态检测主脚本中的主日志器,将模块日志器的名称设置为主日志器名称的子级(比如主日志器是main,模块日志器就设为main.module;主日志器是root,模块日志器设为.module),再配合自定义日志格式处理root的名称显示,就能实现你想要的输出效果。

具体代码实现

1. 模块文件 module.py

import logging
import sys

def get_main_logger():
    """自动获取主脚本中的主日志器"""
    main_mod = sys.modules['__main__']
    # 遍历主脚本的全局变量,找到第一个Logger实例
    for name, obj in main_mod.__dict__.items():
        if isinstance(obj, logging.Logger):
            return obj
    # 如果没找到,默认返回root日志器
    return logging.getLogger()

# 在模块导入时自动获取主日志器并创建子级日志器
main_logger = get_main_logger()
if main_logger.name:
    mod_log_name = f"{main_logger.name}.{__name__}"
else:
    # root日志器名称为空,模块日志器名称以点开头,方便后续格式替换
    mod_log_name = f".{__name__}"
mod_log = logging.getLogger(mod_log_name)

def test():
    mod_log.info("Test Mod")

2. 主脚本 a.py

import logging
import module

# 自定义日志格式,处理root名称为空和模块日志器名称的显示
class CustomFormatter(logging.Formatter):
    def format(self, record):
        if not record.name:
            # 将root日志器的空名称替换为"ROOT"
            record.name = 'ROOT'
        elif record.name.startswith('.'):
            # 将以点开头的模块日志器名称替换为"ROOT.xxx"
            record.name = 'ROOT' + record.name
        return super().format(record)

# 配置主日志器(root)
log = logging.getLogger()
log.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(CustomFormatter('%(name)s - %(message)s'))
log.addHandler(handler)

log.info("Test Main")
module.test()

3. 主脚本 b.py

import logging
import module

# 使用和a.py相同的自定义格式
class CustomFormatter(logging.Formatter):
    def format(self, record):
        if not record.name:
            record.name = 'ROOT'
        elif record.name.startswith('.'):
            record.name = 'ROOT' + record.name
        return super().format(record)

# 配置自定义主日志器"main"
log = logging.getLogger('main')
log.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(CustomFormatter('%(name)s - %(message)s'))
log.addHandler(handler)

log.info("Test Main")
module.test()

运行效果

  • 执行 a.py,输出:
    ROOT - Test Main
    ROOT.module - Test Mod
    
  • 执行 b.py,输出:
    main - Test Main
    main.module - Test Mod
    

注意事项

  • 如果主脚本中存在多个logging.Logger实例,get_main_logger()会返回第一个找到的实例。如果需要更精确的控制,可以约定主日志器的变量名(比如固定用main_log),然后在函数中直接查找该变量。
  • 确保主脚本的日志器在模块导入前或导入后创建都可以:如果先导入模块再创建日志器,get_main_logger()会返回root(和主脚本的root一致);如果先创建日志器再导入模块,会直接找到该日志器。

内容的提问来源于stack exchange,提问作者Drew Aschenbrener

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 08:52:53