我已经创建了一个日志处理程序,它记录到一个数据库表中,我还想将程序发出的警告捕获到另一个表中,所以使用了warnings.captureWarnings(True)
我在py.warnings
日志记录器中的消息格式方面遇到了一些问题-消息总是显示为%s
。我将其三角定位到日志库代码中的此行(logger.warning("%s", s)
),当captureWarnings
为True时,该代码实现警告发射。
当它被替换为logger.warning(s)
时,消息显示如预期。因此,我推测这是某种格式问题,但我不知道是什么。
我还看到了BPO-46557,它实际上实现了这个行为,似乎是出于不相关的原因(但只是针对Python3.11+,我在3.10上)。
下面是我的DB日志处理程序代码。有没有办法在不升级到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')
1条答案
按热度按时间jum4pzuy1#
看起来您正在阅读LogRecord上的
msg
属性并将其存储,而您不应该这样做。(根据文件,特别是它对msg
和message
属性的说明。)它包含一个可能未格式化的字符串,缺少用户提供的参数。在警告捕获的情况下,整个消息作为参数提供,正如您已经找到的那样。替换您的行:
msg=record.__dict__['msg']
与msg=record.getMessage()
,它将按预期工作。