Python 使用SQLAlchemy记录金字塔异常-命令未提交

Python 使用SQLAlchemy记录金字塔异常-命令未提交,python,sqlalchemy,pyramid,Python,Sqlalchemy,Pyramid,我正在使用金字塔web框架和SQLAlchemy,连接到MySQL后端。我组装的应用程序可以工作,但我正试图通过一些增强的日志记录和异常处理来添加一些改进 我基于金字塔网站上的基本SQLAlchemy教程,使用如下会话: DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) 使用DBSession进行查询非常有效,如果我需要向数据库添加和提交一些内容,我将执行以下操作 DBSession.a

我正在使用金字塔web框架和SQLAlchemy,连接到MySQL后端。我组装的应用程序可以工作,但我正试图通过一些增强的日志记录和异常处理来添加一些改进

我基于金字塔网站上的基本SQLAlchemy教程,使用如下会话:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
使用DBSession进行查询非常有效,如果我需要向数据库添加和提交一些内容,我将执行以下操作

DBSession.add(myobject)
DBSession.flush()
所以我得到了我的新身份证

然后我想把日志记录添加到数据库中,所以我就这样做了。这似乎很有效。我一开始在提交时确实遇到了一些奇怪的事情,我不确定SQLAlchemy是如何工作的,所以我将“transaction.commit()”更改为“DBSession.flush()”,以强制提交日志(这在下面讨论!)

接下来,我想添加自定义异常处理,以便为未被显式捕获的任何内容设置友好的错误页面,并记录这些内容。因此,根据我创建的错误处理程序如下:

from pyramid.view import (
    view_config,
    forbidden_view_config,
    notfound_view_config
    )

from pyramid.httpexceptions import (
    HTTPFound,
    HTTPNotFound,
    HTTPForbidden,
    HTTPBadRequest,
    HTTPInternalServerError
    )

from models import DBSession

import transaction
import logging

log = logging.getLogger(__name__)

#region Custom HTTP Errors and Exceptions
@view_config(context=HTTPNotFound, renderer='HTTPNotFound.mako')
def notfound(request):
    log.exception('404 not found: {0}'.format(str(request.url)))
    request.response.status_int = 404
    return {}

@view_config(context=HTTPInternalServerError, renderer='HTTPInternalServerError.mako')
def internalerror(request):
    log.exception('HTTPInternalServerError: {0}'.format(str(request.url)))
    request.response.status_int = 500
    return {}

@view_config(context=Exception, renderer="HTTPExceptionCaught.mako")
def error_view(exc, request):
    log.exception('HTTPException: {0}'.format(str(request.url)))
    log.exception(exc.message)

    return {}
#endregion
所以现在我的问题是,异常被捕获,我的自定义异常视图按预期显示。但是异常不会记录到数据库中。这似乎是因为DBSession事务在任何异常情况下都会回滚。因此,我将日志处理程序改回“transaction.commit”。这实际上是将我的异常日志提交到数据库,但现在任何log语句之后的任何DBSession操作都会抛出“实例未绑定到会话”错误…这是有意义的,因为根据我在事务.commit()之后的理解,会话被清除。控制台日志始终准确显示我希望记录的内容,包括将日志信息写入数据库的SQL语句。但除非我使用transaction.commit(),否则它不会在异常时提交,但如果我这样做,那么我会在transaction.commit()之后杀死任何DBSession语句

Sooooo…我应该如何设置才能登录到数据库,同时也可以捕获并成功记录数据库的异常?我觉得我想让日志处理程序使用某种独立的数据库会话/连接/实例/某种东西,这样它是自包含的,但我不清楚它是如何工作的

或者我应该设计我想做的完全不同的事情

编辑: 最后,我使用了一个单独的、特定于日志的会话,专门用于向数据库添加提交日志信息。在我开始将Pyramid控制台脚本集成到mix中之前,这似乎工作得很好。在mix中,我遇到了脚本中的会话和数据库提交问题,它们不一定像实际的Pyramid web应用程序中那样工作


事后来看(以及我现在所做的),我使用标准的日志和文件处理程序(特别是TimedRotatingFileHandlers)而不是登录到数据库,并登录到文件系统

使用
transaction.commit()
也会对提交的其他模型产生意外的副作用,这并不太酷-使用ZopeTransactionExtension的“正常”金字塔会话设置背后的思想是,单个会话在请求开始时启动,然后如果所有操作都成功,则会提交会话,如果出现异常,则所有内容都将回滚。最好保持这种逻辑,避免在请求中手动提交。

(作为旁注-DBSession.flush()不会提交事务,它会发出SQL语句,但事务可以稍后回滚)

对于异常日志之类的事情,我将考虑设置一个单独的会话,该会话不绑定到Pyramid的请求/响应周期(没有ZopeTransactionExtension),然后使用它创建日志记录。添加日志记录后,需要手动提交事务:

record = Log("blah")
log_session.add(record)
log_session.commit()

谢谢这与我认为可行的方法是一致的。您提到没有绑定到ZopeTransactionExtension,但是是否值得在日志处理程序本身中创建一个新会话,比如LogSession=scoped_session(sessionmaker(extension=ZopeTransactionExtension()),但维护了ZTE,因为回滚行为仍然可能有用?或者,中兴通讯是否与请求周期紧密相关?我肯定遗漏了一些东西,但如果您试图记录错误,我不认为在出现错误时回滚事务会有什么用处:)