【问题标题】:Logging SqlAlchemy to Database将 SqlAlchemy 记录到数据库
【发布时间】:2019-02-08 20:43:36
【问题描述】:

我正在编写一个小型应用程序,它应该使用 sqlalchemy 将所有内容记录到数据库中。 受此启发:

python logging to database

还有这个:

https://docs.pylonsproject.org/projects/pyramid-cookbook/en/latest/logging/sqlalchemy_logger.html 我想出了一个解决方案,该解决方案适用于所有相关库的所有日志消息,除了那些由 s​​qlalchemy (!) 本身生成的日志消息。

这是一个最小的例子,重现了我的问题:

import logging
import datetime
from sqlalchemy import Column, DateTime, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()


 # define table
class TblLog(Base):
    __tablename__ = 'Tbl_Log'

    LOG_TIME = Column(DateTime, primary_key=True)
    LOG_NAME = Column(String(100))
    LOG_LEVEL = Column(String(100))
    LOG_MSG = Column(String(2000))

    def __init__(self, time, name, lvl, msg):
        self.LOG_TIME = time
        self.LOG_NAME = name
        self.LOG_LEVEL = lvl
        self.LOG_MSG = msg

# custom log handler that emits to the database
class DatabaseHandler(logging.Handler):

    def __init__(self, session):
        super().__init__()
        self.session = session
        self.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
        self.setLevel(logging.DEBUG)

    def emit(self, record):

        self.format(record)

        log_time = datetime.datetime.strptime(record.__dict__['asctime'], "%Y-%m-%d %H:%M:%S,%f")

        log_record = TblLog(log_time, record.__dict__['name'], record.__dict__['levelname'], record.__dict__['message'])

        self.session.add(log_record)
        self.session.commit()

在启用 sqlalchemy (!) 日志的情况下对此进行测试:

if __name__ == '__main__':
    # simple logging config
    logging.basicConfig(
        format='%(asctime)s : %(name)s : %(levelname)s : %(message)s',
        level=logging.DEBUG,
    )
    logger_sqlalchemy = logging.getLogger('sqlalchemy')
    logger_sqlalchemy.setLevel(logging.INFO)


    # test with sqlite in memory database
    DB_STRING = 'sqlite:///:memory:'
    engine = create_engine(DB_STRING, echo=False)
    Base.metadata.create_all(engine)
    Session = sessionmaker()
    session = Session(bind=engine)

    # adding custom handler:
    logger_sqlalchemy.addHandler(DatabaseHandler(session))

    logger_sqlalchemy.info('this is a test message')

这引发了

AttributeError: 'NoneType' 对象没有属性 'set'

如有必要,我可以粘贴整个回溯。我怀疑问题出现了 因为 TblLog(...) 调用会产生日志记录,因此处理程序会向自己发出记录?!

这个问题的最佳解决方案是什么,即我可以在处理程序中使用 sqlalchemy 将 sqlalchemy 日志消息写入数据库吗??

我有点卡在这里,谢谢你的帮助......

【问题讨论】:

    标签: python logging sqlalchemy


    【解决方案1】:

    我怀疑问题的出现是因为 TblLog(...) 调用产生 一条日志记录,因此处理程序向自己发出记录?!

    这不是直接的问题。失败的原因是 SQLAlchemy 在配置映射器时发出日志消息,其中第一个是在 TblLog 的映射器完全配置之前发送的,因此是您的错误。

    如果您将StreamHandler 添加到您的logger_sqlalchemy 实例之前 DatabaseHandler 您将能够看到logger_sqlalchemy 在崩溃之前收到的日志消息。触发它的日志消息是(TblLog|Tbl_Log) _post_configure_properties() started,它来自_post_configure_properties() 方法。该方法的文档字符串包括:

    这是一个延迟配置步骤,旨在执行 构建完所有映射器后。

    所以这是TblLog的映射器配置没有完成的线索。

    如果您这样做,请从记录器中删除 DatabaseHandler 并留下 StreamHandler,您将看到该方法继续执行的操作(为了清楚起见,我还删除了您的 basicConfig()):

    (TblLog|Tbl_Log) _post_configure_properties() started
    # this is where your code crashed originally
    (TblLog|Tbl_Log) initialize prop LOG_TIME
    (TblLog|Tbl_Log) initialize prop LOG_NAME
    (TblLog|Tbl_Log) initialize prop LOG_LEVEL
    (TblLog|Tbl_Log) initialize prop LOG_MSG
    (TblLog|Tbl_Log) _post_configure_properties() complete
    

    如您所见,列描述符的一些初始化似乎是在发出第一条日志消息之后发生的。这就是您收到错误的原因,当您尝试使用它时,ORM 还没有准备好。

    您可以实例化一个虚拟的TblLog 实例以强制映射器在添加处理程序之前进行配置,例如:

    # ensure TblLog mapper configured
    TblLog(time=None, name=None, lvl=None, msg=None)
    logger_sqlalchemy.addHandler(DatabaseHandler(session))
    logger_sqlalchemy.info('this is a test message')
    

    但是您会遇到一个新问题:SQLAlchemy 在整个刷新/提交过程中都会发出日志。因此,当第一条日志消息刷新到数据库时,它会生成一条新的日志消息,该消息本身会生成一条新的日志消息等......无限递归。

    所以我的回答是:

    什么是这个问题的最佳解决方案,即我可以写 sqlalchemy 使用 sqlalchemy 将消息记录到数据库 处理程序??

    答案是否定的,如果您还尝试捕获 sqlalchemy 记录日志记录。

    一些可能的解决方案:

    • 不要使用 SQLAlchemy 将日志消息写入数据库。假设您在应用程序的其他部分使用 SQLAlchemy,请直接使用 dpapi 客户端将日志写入数据库,并且明确不要将客户端的日志消息写入数据库(否​​则您最终会遇到相同的递归问题) .
    • 使用HTTPHandler 将您的日志消息发送到网络服务,该HTTPHandler 将日志消息写入数据库。
    • 将 sqlalchemy 记录到文件中,而其他所有内容都记录到数据库中。您甚至可以设置一个 cron 作业,以在单独的进程中定期将文件中的 sqlalchemy 日志写入数据库。
    • Logging as a service

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-13
      • 2016-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-19
      相关资源
      最近更新 更多