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

Python logging.config.dictConfig封装类后日志无法输出到控制台与文件

问题现象

封装Python logging的dictConfig配置类后,预期日志同时输出到控制台与指定路径/tmp/xxxx.log,实际运行未生效;直接执行类内部的配置逻辑时功能可以正常运行。

根因分析

核心问题是类初始化时位置参数传递顺序错误:

  • Get_Logger的__init__方法定义的形参顺序为(logfile, log_handler),即第一个参数接收日志文件路径,第二个参数接收logger名称
  • 实例化时传入的第一个参数是logger名称"debug_mode",第二个参数才是日志路径logfile
  • 最终实际赋值结果为self.logfile = "debug_mode"、self.log_handler = "/tmp/xxxx.log",后续调用logging.getLogger(self.log_handler)获取到的是名为/tmp/xxxx.log的未配置logger,不会加载预先定义的console和localfile handler,日志输出自然不符合预期。

除此之外代码还有两个潜在隐患:

  • 全局定义的logconf_dict是可变对象,每次调用get_logger直接修改全局字典,多次实例化Get_Logger时会出现配置互相覆盖的问题
  • logger配置中propagate设为True,日志会向上传递到根logger,容易出现重复打印的问题
排查步骤
  • 打印实例化后对象的self.logfile、self.log_handler属性,确认赋值是否和预期一致
  • 检查logging.getLogger()传入的名称,是否存在于logconf_dict的loggers配置项中
  • 移除类封装,逐行执行类内逻辑对比变量赋值差异,定位传参错误点
修复方案
  1. 调整形参/实参的传递顺序,优先使用关键字参数传参,从根源避免位置参数顺序错误
  2. 对基础配置模板做深拷贝后再修改,不要直接改动全局配置字典,避免多实例配置污染
  3. 将logger的propagate属性设为False,避免日志重复打印
  4. 配置加载前清理对应logger的已有handler,避免多次调用时重复添加handler导致日志重复输出

修复后的完整可运行代码:

#!/usr/bin/env python3
import copy
import logging
from logging.config import dictConfig

debug = True

class RequireDebugTrue(logging.Filter):
    def filter(self, record):
        return debug 

# 基础配置模板,仅作为初始模板使用,不直接修改
BASE_LOGCONF = {
    'version': 1,
    'disable_existing_loggers': False,

    'formatters': {
        'debug_fmt': {
            'format': '[%(asctime)s][%(threadName)s:%(thread)d][%(name)s:'
                '%(levelname)s][%(module)s:%(funcName)s(%(lineno)d)]:%(message)s'
        },
        'prod_fmt': {
            'format': '[%(asctime)s][%(name)s:%(levelname)s]'
                '[%(module)s:%(funcName)s(%(lineno)d)]:%(message)s'
        }
    },

    'filters': {
        'require_debug_true': {
            '()': RequireDebugTrue,
        }
    },

    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'debug_fmt',
            'filters': ['require_debug_true']
        },
        'localfile': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'formatter': 'prod_fmt',
            'filename': '', # 占位符,运行时动态替换
            'maxBytes': 1024 * 1024 * 5,
            'backupCount': 5,
            'encoding': 'utf8'
        },
    },

    'loggers': {
        'debug_mode': {
            'handlers': ['console', 'localfile'],
            'level': 'DEBUG',
            'propagate': False, # 关闭向上传播,避免重复打印
        },
        'prod_mode': {
            'handlers': ['localfile'],
            'level': 'DEBUG',
            'propagate': False,
        },
    }
}

class Get_Logger(object):
    def __init__(self, log_handler, logfile):
        # 调整形参顺序符合使用习惯:第一个传logger名称,第二个传日志路径
        self.log_handler = log_handler
        self.logfile = logfile

    def get_logger(self):
        # 深拷贝基础模板,不修改全局配置
        log_conf = copy.deepcopy(BASE_LOGCONF)
        log_conf["handlers"]["localfile"]["filename"] = self.logfile
        logger = logging.getLogger(self.log_handler)
        # 清理已有handler避免重复打印
        logger.handlers.clear()
        dictConfig(log_conf)
        return logger

if __name__ == '__main__':
    logfile = "/tmp/xxxx.log"
    # 推荐用关键字参数传参,彻底避免顺序错误:Get_Logger(log_handler="debug_mode", logfile=logfile)
    logger_obj = Get_Logger("debug_mode", logfile)
    logger = logger_obj.get_logger()
    logger.debug("This is one test.")
额外优化建议
  • 多次调用日志配置逻辑时,增加判断避免重复执行dictConfig,减少不必要的性能开销
  • 日志文件路径建议做路径存在性校验,自动创建父目录避免因目录不存在导致文件写入失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 05:09:33