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

如何在类间传递自定义Logger并按需更新日志器名称

自定义日志器类的传递与命名问题解决

问题概述

封装了自定义myLogger类后,希望通过__init__(self, logger)在不同模块间传递日志器实例,同时根据使用位置生成层级化的日志器名称(如main、main.subfolder1.sub1),实现同一日志文件内的模块区分。但当前实现存在两个问题:修改日志器名称会全局生效,且传递时会重复追加名称,无法达到预期的日志格式。

原myLogger代码如下:

import logging

class myLogger:
    def __init__(self, name):
        # levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
        logging.basicConfig(
            level = logging.DEBUG,
            format = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
            datefmt = '%m-%d %H:%M',
            filename = '/tmp/myLog.log',
            filemode = 'a'
            )

        self.logger = logging.getLogger(name)
        self.name = name

    def formatString(self, msg, *args, **kwargs):
        """
        Capitalizes the first letter of a string
        """
        return msg[0].upper() + msg[1:]

    def debug(self, msg=''):
        """
        Wrapper for logger.debug method. Formats the debug message to ensure it writes
        to the log file.
        """
        if not msg:
            raise(ValueError("DEBUG message cannot be blank!"))

        if not msg[0].isupper():
            msg = self.formatString(msg)

        self.logger.debug(msg)

    def info(self, msg=''):
        """
        Wrapper for logger.info method. Formats the debug message to ensure it writes
        to the log file.
        """
        if not msg:
            raise(ValueError("INFO message cannot be blank!"))

        if not msg[0].isupper():
            msg = self.formatString(msg)

        self.logger.info(msg)

    def warning(self, msg=''):
        """
        Wrapper for logger.warning method. Formats the debug message to ensure it writes
        to the log file.
        """
        if not msg:
            raise(ValueError("WARNING message cannot be blank!"))

        if not msg[0].isupper():
            msg = self.formatString(msg)

        self.logger.warning(msg)

    def error(self, msg=''):
        """
        Wrapper for logger.error method. Formats the debug message to ensure it writes
        to the log file.
        """
        if not msg:
            raise(ValueError("ERROR message cannot be blank!"))
        if not msg[0].isupper():
            msg = self.formatString(msg)

        self.logger.error(msg)

    def critical(self, msg=''):
        """
        Wrapper for logger.critical method. Formats the debug message to ensure it writes
        to the log file.
        """
        if not msg:
            raise(ValueError("CRITICAL message cannot be blank!"))

        if not msg[0].isupper():
            msg = self.formatString(msg)

        self.logger.critical(msg)

    def getLogger(self, name):
        self.logger = logging.getLogger("{}.{}".format(self.logger.name, name))

问题根源

  1. 全局配置重复执行:logging.basicConfig在__init__中被调用,每次实例化myLogger都会执行,但该函数仅第一次调用生效,后续调用无意义,还可能引发配置混乱。
  2. 日志器实例被修改:getLogger方法直接替换当前实例的self.logger,导致原实例的日志器被篡改,出现"全局生效"的问题。
  3. 子日志器创建方式错误:手动拼接名称创建子日志器,容易出现重复追加的问题,不符合logging模块的层级设计逻辑。

修复方案

修改后的myLogger类

重新设计初始化逻辑,支持两种创建方式:传入根名称生成根日志器,或传入父日志器实例生成子日志器;分离全局配置与实例化,使用官方推荐的getChild()方法创建层级日志器,避免重复追加名称。

import logging

class myLogger:
    def __init__(self, logger_or_name):
        # 支持两种初始化:传入logging.Logger实例,或传入名称创建根日志器
        if isinstance(logger_or_name, logging.Logger):
            self.logger = logger_or_name
        else:
            self.logger = logging.getLogger(logger_or_name)

    @staticmethod
    def setup_global_config():
        # 全局日志配置仅在入口模块调用一次
        logging.basicConfig(
            level=logging.DEBUG,
            format='%(asctime)s %(name)-25s %(levelname)-8s %(message)s',
            datefmt='%m-%d %H:%M',
            filename='/tmp/myLog.log',
            filemode='a'
        )

    def formatString(self, msg):
        """统一格式化日志消息首字母大写"""
        if not msg:
            return msg
        return msg[0].upper() + msg[1:]

    def _log(self, level, msg):
        # 统一日志处理逻辑,减少重复代码
        if not msg:
            raise ValueError(f"{level.upper()} message cannot be blank!")
        if not msg[0].isupper():
            msg = self.formatString(msg)
        getattr(self.logger, level)(msg)

    def debug(self, msg=''):
        self._log('debug', msg)

    def info(self, msg=''):
        self._log('info', msg)

    def warning(self, msg=''):
        self._log('warning', msg)

    def error(self, msg=''):
        self._log('error', msg)

    def critical(self, msg=''):
        self._log('critical', msg)

    def get_child_logger(self, child_name):
        # 创建子日志器并返回新的myLogger实例,不修改当前实例
        child_logger = self.logger.getChild(child_name)
        return myLogger(child_logger)

模块间使用示例

main.py(入口模块)

from myLogger import myLogger
from subfolder1.sub1 import Sub1
from subfolder2.sub2 import Sub2

# 全局日志配置仅执行一次
myLogger.setup_global_config()

# 创建根日志器
root_logger = myLogger("main")
root_logger.error("Error logged: Error message")

# 生成子日志器传递给子模块
sub1 = Sub1(root_logger.get_child_logger("subfolder1.sub1"))
sub1.log_error()

sub2 = Sub2(root_logger.get_child_logger("subfolder2.sub2"))
sub2.log_error()

subfolder1/sub1.py

class Sub1:
    def __init__(self, logger):
        self.logger = logger

    def log_error(self):
        self.logger.error("Error logged: Error message")

subfolder2/sub2.py

class Sub2:
    def __init__(self, logger):
        self.logger = logger

    def log_error(self):
        self.logger.error("Error logged: Error message")

预期日志输出

09-05 18:19 main                     ERROR    Error logged: Error message
09-05 18:19 main.subfolder1.sub1     ERROR    Error logged: Error message
09-05 18:19 main.subfolder2.sub2     ERROR    Error logged: Error message

关键改进点

  • 分离全局配置:setup_global_config作为静态方法,仅在入口模块调用一次,避免重复配置。
  • 层级日志器创建:使用logger.getChild()方法生成子日志器,遵循logging模块的层级设计,不会出现重复追加名称的问题。
  • 实例隔离:创建子日志器时返回新的myLogger实例,原实例的日志器不会被修改,避免全局生效问题。
  • 代码复用:通过_log方法统一处理日志校验与格式化,减少重复代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:07:34