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
相关产品推荐
相关产品推荐

