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

Python自定义带QueueHandler的logger如何跨类跨文件全局复用

核心原理

Python 的 logging 模块是全局层级单例设计,只要正确配置父级日志器,所有子日志器都会自动继承配置,不需要手动传递 logger 对象。你当前的 init 方法只给 custom_logging 模块对应的日志器(getLogger(__name__))加了 handler,其他文件的日志器属于不同的命名节点,自然不会继承这套配置。


方案1:配置根日志器(全局生效,最简单)

直接给根日志器加配置,所有模块的日志器都会默认继承这套规则,适合大部分业务场景。
修改你的 custom_logging.py 代码:

import logging
import queue
import json
import uuid
import requests
from logging.handlers import QueueHandler, QueueListener
from requests.adapters import HTTPAdapter, Retry
import atexit

# 全局持有QueueListener实例,避免被垃圾回收导致日志丢失
_listener = None

class CustomHttpHandler(logging.Handler):
    def __init__(self, url: str, token: str, silent: bool = True):
        self.url = url
        self.token = token
        self.silent = silent
        self.MAX_POOLSIZE = 100
        self.session = requests.Session()
        # 需要header配置的话取消下面注释即可
        # self.session.headers.update({
        #     "Content-Type": "application/json",
        #     "Authorization": "Bearer %s" % (self.token)
        # })
        self.session.mount("https://", HTTPAdapter(
            max_retries=Retry(
                total=5,
                backoff_factor=0.5,
                status_forcelist=[403, 500]
            ),
            pool_connections=self.MAX_POOLSIZE,
            pool_maxsize=self.MAX_POOLSIZE
        ))
        super().__init__()

    def emit(self, record):
        logEntry = self.format(record)
        logEntry=json.loads(logEntry)
        response = self.session.post(self.url, json=logEntry)
        if not self.silent:
            print(logEntry)
            print(response.content)

def init():
    global _listener
    # 避免重复初始化
    if _listener is not None:
        return

    # 取根日志器,所有子日志器都会继承配置
    root_logger = logging.getLogger()

    console_handler = logging.StreamHandler()
    console_formatter = logging.Formatter("%(levelname)s: %(message)s")
    console_handler.setFormatter(console_formatter)

    httpHandler = CustomHttpHandler(
        url="https://someAddress/log",
        token="1234",
        silent=True
    )
    MACAddr=uuid.getnode()
    http_formatter = logging.Formatter(json.dumps({
        "time": "%(asctime)s",
        "MAC Node": MACAddr,
        "line": "%(lineno)d",
        "module":"%(name)s",
        "logLevel": "%(levelname)s",
        "message": "%(message)s"
    }))
    httpHandler.setFormatter(http_formatter)

    log_queue = queue.Queue(-1)
    queue_handler = QueueHandler(log_queue)
    root_logger.addHandler(queue_handler)

    _listener = QueueListener(log_queue, console_handler, httpHandler)
    _listener.start()

    root_logger.setLevel(logging.DEBUG)

    # 注册程序退出钩子,自动停止listener,避免丢日志
    atexit.register(_listener.stop)

    root_logger.info("日志系统初始化完成")

使用方式

  • 在 main.py 启动时先调用一次初始化:
import custom_logging
custom_logging.init()
  • 其他任意文件/类中直接拿当前模块的日志器即可,自动继承配置:
import logging
# __name__会自动替换为当前模块名,日志的module字段会自动显示对应模块
logger = logging.getLogger(__name__)

logger.debug("调试日志")
logger.info("普通日志")

方案2:固定命名空间日志器(不污染全局根日志)

如果不想影响第三方库的日志输出,可以配置指定命名空间的日志器,所有业务代码都从该命名空间取日志器。
在方案1的基础上修改init方法、新增获取日志器的封装方法:

def init():
    global _listener
    if _listener is not None:
        return
    # 配置固定命名的日志器,所有业务代码都取这个前缀的日志器
    app_logger = logging.getLogger("my_app")
    # 禁止向上传递到根日志器,避免重复输出
    app_logger.propagate = False

    console_handler = logging.StreamHandler()
    # 其余handler配置逻辑和方案1完全一致
    # ... 省略重复代码 ...
    app_logger.addHandler(queue_handler)
    app_logger.setLevel(logging.DEBUG)
    atexit.register(_listener.stop)

# 封装获取日志器的方法,统一入口
def get_logger(name: str = None):
    if name:
        return logging.getLogger(f"my_app.{name}")
    return logging.getLogger("my_app")

使用方式

  • 初始化还是在main.py调用custom_logging.init()
  • 其他文件中通过封装方法取日志器:
from custom_logging import get_logger
logger = get_logger(__name__)

logger.info("业务日志")

你之前想到的init返回logger的方式也可以用,但以上两种方案更符合logging模块的设计规范,不需要每个模块都手动导入特定logger对象,灵活性更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 10:36:07