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

Python3.10中warnings.captureWarnings(True)日志格式异常问题排查

Python 3.10中py.warnings日志消息显示为%s的修复方案

我用warnings.captureWarnings(True)将程序警告捕获到数据库的单独表中,但遇到了py.warnings日志的消息始终显示为%s的问题。定位后发现,Python 3.10的logging库在输出警告时使用了logger.warning("%s", s)的写法,导致LogRecord的msg字段固定为%s,实际警告消息存储在record.args中。这个问题在Python 3.11+中已通过BPO-46557修复,但如果不想升级版本,可以用以下几种方案解决:

方法1:在Handler中获取格式化后的完整消息

直接利用LogRecord.getMessage()方法获取已经格式化好的完整消息,代替直接读取record.msg。这是最简便的方案,只需修改自己的日志处理器代码。

修改SQLAlchemyHandler的emit方法:

def emit(self, record):
    trace = None
    exc = record.__dict__['exc_info']
    if exc:
        trace = traceback.format_exc()
    log = self.log_class(
        logger=record.__dict__['name'],
        level=record.__dict__['levelname'],
        trace=trace,
        msg=record.getMessage(),  # 替换原来的record.__dict__['msg']
        source_loc=f"{record.__dict__['pathname']}:{record.__dict__['lineno']}")
    self.session.add(log)
    self.session.commit()

方法2:Monkey-Patch warnings模块的日志输出逻辑

直接修改warnings模块中发送日志的逻辑,替换成不使用格式化占位符的调用方式,从源头解决问题。

在初始化logging的代码前添加以下代码:

import warnings
import logging
import linecache

def _showwarning(message, category, filename, lineno, file=None, line=None):
    if file is not None:
        if line is None:
            line = linecache.getline(filename, lineno)
        warnings._showwarnmsg_impl(message, category, filename, lineno, file, line)
    else:
        s = warnings._formatwarnmsg_impl(message, category, filename, lineno, line)
        logger = logging.getLogger("py.warnings")
        # 直接传入完整消息,不再使用%s格式化
        logger.warning(s)

warnings.showwarning = _showwarning

# 后续的logging初始化代码
logging.captureWarnings(True)
warnings_logger = logging.getLogger("py.warnings")
warnings_logger.setLevel(logging.DEBUG)
from db_logging import SQLAlchemyWarningHandler
handler = SQLAlchemyWarningHandler()
warnings_logger.addHandler(handler)

方法3:给py.warnings日志添加过滤器修正LogRecord

创建一个日志过滤器,在日志被处理器处理前,修正py.warnings对应的LogRecord,将msg替换为实际消息内容。

import logging

class FixWarningsLogFilter(logging.Filter):
    def filter(self, record):
        if record.name == "py.warnings" and record.msg == "%s" and record.args:
            # 替换msg为实际消息,清空args避免后续格式化问题
            record.msg = record.args[0]
            record.args = ()
        return True

# 在添加Handler前注册过滤器
logging.captureWarnings(True)
warnings_logger = logging.getLogger("py.warnings")
warnings_logger.setLevel(logging.DEBUG)
# 添加过滤器
warnings_logger.addFilter(FixWarningsLogFilter())
from db_logging import SQLAlchemyWarningHandler
handler = SQLAlchemyWarningHandler()
warnings_logger.addHandler(handler)

方案对比

  • 方法1:侵入性最低,仅修改自身处理器代码,不影响其他日志逻辑,推荐优先使用。
  • 方法2:彻底修复源头问题,所有处理py.warnings的Handler都会受益,但属于Monkey-Patch,需注意兼容性。
  • 方法3:通过过滤器修正日志记录,对所有绑定到py.warnings的Handler生效,适合多Handler场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 23:00:41