非__main__中实例化的Logger无法接收被调用模块日志如何解决
问题解答
根因分析
Python的logging模块采用层级传播机制,你的问题由两个配置缺失导致:
- 单独运行CLI时,你调用了
logging.basicConfig(level=logging.INFO)将根logger的默认级别从WARNING修改为了INFO,子模块输出的INFO级日志可以正常通过根logger的Handler输出;但UI调用时没有执行CLI的__main__逻辑,也没有配置根logger级别,子模块的INFO日志会被默认过滤。 - 你仅给名称为
SPECIAL_NAME_X的CLI专属logger添加了UI展示的Handler,但子模块的logger名称为对应模块名(比如Foo模块的logger名就是Foo),和SPECIAL_NAME_X的logger没有层级归属关系,日志不会自动传播到你添加的Handler中。
解决方案
场景1:三个CLI的日志不需要分开展示
直接全局配置根logger即可,无需修改原有业务代码:
import logging # 替换为你自己实现的、发信号到QTextEdit的Handler from your_module import QtLogHandler logging.basicConfig( level=logging.INFO, handlers=[QtLogHandler()] )
所有模块的INFO及以上级别日志都会被捕获并输出到UI控件。
场景2:三个CLI的日志需要分别展示在不同的UI控件
因为每个CLI运行在独立线程中,可以通过线程标识+日志过滤器实现,同样无需修改原有业务代码:
- 首先实现日志过滤器,只放行指定线程产生的日志:
def thread_log_filter(thread_id): def filter_func(record): return record.thread == thread_id return filter_func
- 全局配置根logger级别为INFO,避免子模块日志被过滤:
import logging logging.basicConfig(level=logging.INFO)
- 启动每个CLI线程时,给对应UI的Handler绑定线程过滤器:
import threading from your_module import QtLogHandler # 以CLI1为例 # 启动CLI业务线程 cli1_thread = threading.Thread(target=cli1_run_logic) cli1_thread.start() # 获取当前CLI线程的ID cli1_thread_id = cli1_thread.ident # 初始化对应UI的日志Handler cli1_handler = QtLogHandler(your_qtextedit_widget) # 给Handler添加过滤器,只接收当前CLI线程的日志 cli1_handler.addFilter(thread_log_filter(cli1_thread_id)) # 将Handler添加到根logger logging.getLogger().addHandler(cli1_handler)
按相同逻辑配置另外两个CLI的Handler即可,每个Handler只会捕获对应线程产生的所有日志,包含子模块输出的内容。
备选方案(仅适合子模块数量极少的场景)
如果你的CLI依赖的子模块数量很少,可以直接给子模块的logger添加对应CLI的Handler,同时设置日志级别:
# 配置CLI1的日志 cli1_logger = logging.getLogger('SPECIAL_NAME_1') cli1_logger.setLevel(logging.INFO) cli1_handler = QtLogHandler() cli1_logger.addHandler(cli1_handler) # 给所有依赖的子模块logger添加同一个Handler foo_logger = logging.getLogger('Foo') foo_logger.setLevel(logging.INFO) foo_logger.addHandler(cli1_handler)
注意:如果多个CLI共用同一个子模块,该方案会导致子模块日志重复输出到多个CLI的日志控件,不推荐多CLI共用模块的场景使用。
内容的提问来源于stack exchange,提问作者KDecker
相关产品推荐
相关产品推荐

