设置 warnings.captureWarnings(True) 时避免出现格式问题

问题描述 投票:0回答:1

我创建了一个日志处理程序,它记录到数据库表中,并且我还想将程序发出的警告捕获到不同的表中,因此使用了

warnings.captureWarnings(True)

我在

py.warnings
记录器中的消息格式存在一些问题 - 消息始终显示为
%s
。我将其三角测量到日志库代码中的这条线
logger.warning("%s", s)
),当
captureWarnings
为True时实现警告发射。

当它被

logger.warning(s)
替换时,消息将按预期显示。因此,我认为这是某种格式问题,但我不知道是什么。

我还看到了 BPO-46557 它实际上实现了这种行为,似乎是出于不相关的原因(但仅适用于 Python3.11+,我使用的是 3.10)。

我的数据库日志处理程序代码如下。有没有办法在不升级到 Python 3.11 的情况下解决这个问题 - 似乎没有必要。

在主代码运行之前调用的代码:

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)

处理程序和 LogRecord 代码 (

db_logging.py
)。然而,我不相信这段代码中有任何内容,因为我得到:
<LogRecord: py.warnings, 30, /usr/lib/python3.10/warnings.py, 110, "%s">
当我在发出记录之前打印出记录时

from database import database

# lib imports
from sqlalchemy import Column
from sqlalchemy.types import DateTime, Integer, String
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.orm import sessionmaker
from sqlalchemy import DDL, event

# stdlib imports
import logging
import traceback
import sys

class Base(object):
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

    __table_args__ = {"schema": "logs"}
    id = Column(Integer, primary_key=True) # auto incrementing
    logger = Column(String) # the name of the logger. (e.g. myapp.views)
    level = Column(String) # info, debug, or error?
    trace = Column(String) # the full traceback printout
    msg = Column(String) # any custom log you may have included
    created_at = Column(DateTime, default=func.now()) # the current timestamp
    source_loc = Column(String)

    def __init__(self, logger=None, level=None, trace=None, msg=None, source_loc=None):
        self.logger = logger
        self.level = level
        self.trace = trace
        self.msg = msg
        self.source_loc = source_loc

    def __unicode__(self):
        return self.__repr__()

    def __repr__(self):
        return "<Log: %s - %s>" % (self.created_at.strftime('%m/%d/%Y-%H:%M:%S'), self.msg[:50])



Base = declarative_base(cls=Base)
event.listen(Base.metadata, 'before_create', DDL("CREATE SCHEMA IF NOT EXISTS logs"))

class Logs(Base):
    "log class which writes all main db logs"
    pass

class WarningLogs(Base):
    "seperate log class for deprecation warnings which writes to a different db table"
    pass

class SQLAlchemyHandler(logging.Handler):
    "A very basic logger that commits a LogRecord to the SQL Db"
    def __init__(self):
        logging.Handler.__init__(self)
        Base.metadata.create_all(database.engine)
        Session = sessionmaker(bind=database.engine)
        self.session = Session()
        self.log_class = getattr(sys.modules[__name__], 'Logs')

    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.__dict__['msg'],
            source_loc=f"{record.__dict__['pathname']}:{record.__dict__['lineno']}")
        self.session.add(log)
        self.session.commit()

    class SQLAlchemyWarningHandler(SQLAlchemyHandler):
    "Extends SQLAlchemyHandler to use WarningLog objects, which use a different table"
    def __init__(self,  **kwargs):
        super().__init__(**kwargs)
        self.log_class = getattr(sys.modules[__name__], 'WarningLogs')
python logging sqlalchemy warnings python-logging
1个回答
1
投票

看起来您正在读取 LogRecord 上的

msg
属性并存储它,但您不应该这样做。 (根据 documentation,特别是关于
msg
message
属性的说明。)它包含可能未格式化的字符串,缺少用户提供的参数。在警告捕获的情况下,整个消息将作为参数提供,正如您已经发现的那样。

更换线路:

msg=record.__dict__['msg']
msg=record.getMessage()
一起使用,它将按预期工作。

© www.soinside.com 2019 - 2024. All rights reserved.