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

Python自定义Logger重复打印问题及logging.basicConfig影响消除

自定义Logger日志重复打印问题及解决方案

问题背景

使用openshift-restclient-python库时,自定义Logger出现日志重复打印的情况。调用logging.basicConfig(level=logging.DEBUG)后,即使移除自定义Handler,日志仍会重复输出。

代码场景

自定义Logger代码(modules/logging/Logging.py)

import logging
class CustomLogger:
    logger=None
    def __init__(self):
        if (CustomLogger.logger==None):
            logger = logging.getLogger(__name__)
            logger.setLevel(logging.DEBUG)
            stdout_handler = logging.StreamHandler()
            stdout_handler.setLevel(logging.DEBUG)
            logger.addHandler(stdout_handler)
            CustomLogger.logger=logger

    def getLogger(self):
        return CustomLogger.logger

logger = CustomLogger().getLogger()

主程序代码(main.py)

#!/usr/bin/env python3
import sys
from modules.logging.Logging import logger
from kubernetes import client, config
from openshift.dynamic import DynamicClient
from openshift.helper.userpassauth import OCPLoginConfiguration
import warnings
warnings.filterwarnings("ignore")
apihost = 'myhost'
username = 'myuser'
password = 'insecure'
ca_cert = '/path/to/cert'
kubeConfig = OCPLoginConfiguration(ocp_username=username, ocp_password=password)
kubeConfig.host = apihost
kubeConfig.verify_ssl = False
kubeConfig.ssl_ca_cert = ca_cert
kubeConfig.get_token()
k8s_client = client.ApiClient(kubeConfig)
logger.warning("this is printed once")
dyn_client = DynamicClient(k8s_client)
logger.warning("this is printed twice")
v1_projects = dyn_client.resources.get(api_version='project.openshift.io/v1', kind='Project')
project_list = v1_projects.get()
sys.exit(0)

执行输出

this is printed once
ERROR:root:load cache error: ResourceList.__init__() got an unexpected keyword argument 'base_resource_lookup'
this is printed twice
WARNING:modules.logging.Logging:this is printed twice

自定义Logger存在的问题

  • 未禁用日志传播:Python Logger默认会将日志向上传播到父级Logger(如root Logger)。当openshift-restclient-python内部调用logging.basicConfig()后,root Logger会自动添加一个StreamHandler,此时自定义Logger的日志会同时被自身Handler和root Handler处理,导致重复打印。
  • 单例实现细节疏漏:虽然用类属性实现单例,但未考虑多线程场景下的竞态条件,不过这不是本次重复打印的直接原因。

解决方案

1. 修复自定义Logger,禁用日志传播

修改modules/logging/Logging.py,添加logger.propagate = False阻止日志向上传播,同时可自定义日志格式避免混淆:

import logging
class CustomLogger:
    logger=None
    def __init__(self):
        if CustomLogger.logger is None:
            logger = logging.getLogger(__name__)
            logger.setLevel(logging.DEBUG)
            # 禁用日志传播,避免被root Logger重复处理
            logger.propagate = False
            stdout_handler = logging.StreamHandler()
            stdout_handler.setLevel(logging.DEBUG)
            # 自定义日志格式
            formatter = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
            stdout_handler.setFormatter(formatter)
            logger.addHandler(stdout_handler)
            CustomLogger.logger=logger

    def getLogger(self):
        return CustomLogger.logger

logger = CustomLogger().getLogger()

2. 清除root Logger的已初始化Handler

如果第三方库已调用logging.basicConfig(),可在自定义Logger初始化时清除root Logger的所有Handler,避免其输出日志:

# 在CustomLogger的__init__中添加以下代码
root_logger = logging.getLogger()
for handler in root_logger.handlers[:]:
    root_logger.removeHandler(handler)

3. 验证效果

修改后执行main.py,日志将仅打印一次:

WARNING:modules.logging.Logging:this is printed once
ERROR:root:load cache error: ResourceList.__init__() got an unexpected keyword argument 'base_resource_lookup'
WARNING:modules.logging.Logging:this is printed twice

若需隐藏root的ERROR日志,可进一步将root Logger级别设为logging.CRITICAL,或直接禁用其输出。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 20:25:11