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

Python 3中基类日志Handler仅添加一次的Pythonic实现及日志拓展问题

Python日志Handler单次执行的Pythonic实现及__main__日志记录问题

我正在尝试记录当前运行的完整类名和方法。在下方的示例代码中,我通过Base类的类属性log_handler_added来跟踪日志Handler是否已添加:如果没有这个控制逻辑,基类每被继承一次就会输出一条重复日志,错误输出示例如下:

1688489421.224986 DEBUG Base.TestClassA.testmethod1 |debug message from test class A|
1688489421.225088  INFO Base.TestClassB.testmethod2 |info message from test class B|
1688489421.225088  INFO Base.TestClassB.testmethod2 |info message from test class B|
1688489421.225162 ERROR Base.TestClassC.another_method |error message from test class C|
1688489421.225162 ERROR Base.TestClassC.another_method |error message from test class C|
1688489421.225162 ERROR Base.TestClassC.another_method |error message from test class C|

我在__init__方法末尾添加判断逻辑,仅调用一次self.__logger.addHandler(console_handler)后,得到了正确的输出:

1688490430.369992 DEBUG Base.TestClassA.testmethod1 |debug message from test class A|
1688490430.370093  INFO Base.TestClassB.testmethod2 |info message from test class B|
1688490430.370155 ERROR Base.TestClassC.another_method |error message from test class C|

但我觉得这种实现方式不够健壮,作为新手缺乏相关经验判断,因此有两个疑问:

  • 调用self.__logger.addHandler(console_handler)仅执行一次的正确Pythonic方式是什么?
  • 能否利用当前的元类+继承机制在__main__中记录日志?

示例代码如下:

#!/usr/bin/env python3

import logging


class MetaBase(type):
    def __init__(cls, *args):
        super().__init__(*args)

        # 显式命名改写
        logger_attribute_name = "_" + cls.__name__ + "__logger"

        # 考虑继承关系生成日志器名称
        logger_name = ".".join([c.__name__ for c in cls.mro()[-2::-1]])

        setattr(cls, logger_attribute_name, logging.getLogger(logger_name))


class Base(metaclass=MetaBase):
    log_handler_added = False

    def __init__(self):
        log_formatter = logging.Formatter(
            "{created:<f} {levelname:>5s} {name}.{funcName:>8s} |{message}|",
            style="{",
        )
        loglevel = "DEBUG"

        # 将日志级别字符串转为对应的数值
        numeric_level = getattr(logging, loglevel.upper(), None)
        if not isinstance(numeric_level, int):
            raise ValueError("Invalid log level: %s" % loglevel)

        # 仅输出到 stdout/stderr
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(log_formatter)
        self.__logger.setLevel(numeric_level)

        # 使用类属性控制仅添加一次Handler,避免基类每次被继承都重复添加
        if not Base.log_handler_added:
            self.__logger.addHandler(console_handler)
            Base.log_handler_added = True


class TestClassA(Base):
    def testmethod1(self):
        self.__logger.debug("debug message from test class A")


class TestClassB(Base):
    def testmethod2(self):
        self.__logger.info("info message from test class B")


class TestClassC(Base):
    def another_method(self):
        self.__logger.error("error message from test class C")


a = TestClassA()
a.testmethod1()

b = TestClassB()
b.testmethod2()

p = TestClassC()
p.another_method()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:41:59