如何在类间传递自定义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))
问题根源
- 全局配置重复执行:
logging.basicConfig在__init__中被调用,每次实例化myLogger都会执行,但该函数仅第一次调用生效,后续调用无意义,还可能引发配置混乱。 - 日志器实例被修改:
getLogger方法直接替换当前实例的self.logger,导致原实例的日志器被篡改,出现"全局生效"的问题。 - 子日志器创建方式错误:手动拼接名称创建子日志器,容易出现重复追加的问题,不符合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
相关产品推荐
相关产品推荐

