使用SQLAlchemy进行金字塔异常日志记录 - 命令不提交

时间:2013-02-12 03:06:09

标签: python sqlalchemy pyramid

我正在使用Pyramid Web框架和SQLAlchemy,连接到MySQL后端。我把这个应用程序放在一起工作,但我试图通过一些增强的日志记录和异常处理来增加一些润色。

我将所有内容都基于Pyramid网站上的基本SQLAlchemy教程,使用类似的会话:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))

使用DBSession进行查询效果很好,如果我需要向数据库添加和提交内容,我会做类似的事情

DBSession.add(myobject)
DBSession.flush()

所以我得到了我的新身份证。

然后我想添加日志记录到数据库,所以我跟着this tutorial。这似乎很有效。我最初遇到一些奇怪的事情已经提交,我不确定SQLAlchemy是如何工作的所以我已经将“transaction.commit()”更改为“DBSession.flush()”以强制日志提交(这是解决的问题)下面!)。

接下来我想添加自定义异常处理,意图是我可以为未明确捕获的任何内容添加一个友好的错误页面并仍然记录事物。所以基于this documentation,我创建了错误处理程序,如下所示:

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”。这具有实际将我的异常日志提交到数据库的效果,但是现在任何日志语句之后的任何DBSession操作都会抛出“未绑定到会话的实例”错误......这是有道理的,因为根据我的理解,在transaction.com之后()会话被清除。控制台日志始终显示我想要记录的内容,包括将日志信息写入数据库的SQL语句。但是除非我使用transaction.commit(),否则它不会在异常上提交,但如果我这样做,那么我会在transaction.commit()之后删除任何DBSession语句!。

Sooooo ....如何设置以便我可以登录到数据库,还可以捕获并成功记录数据库的异常?我觉得我希望日志记录处理程序使用某种单独的数据库会话/连接/实例/某些东西,以便它是自包含的,但我不清楚它是如何工作的。

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

编辑: 我最终选择了一个单独的,特定于日志的会话,专门用于向数据库添加提交日志信息。这似乎很有效,直到我开始将Pyramid控制台脚本集成到混合中,其中我遇到了脚本中的会话和数据库提交问题,不一定像在实际的Pyramid Web应用程序中那样工作。

事后看来(以及我现在正在做的事情)而不是记录到数据库我使用标准日志记录和FileHandlers(具体是TimedRotatingFileHandlers)并登录到文件系统。

1 个答案:

答案 0 :(得分:2)

使用transaction.commit()对其他模型的更改产生了意想不到的副作用,这也不太酷 - 使用ZopeTransactionExtension设置“普通”Pyramid会话背后的想法是单个会话从请求的开始,如果一切都成功,则会提交会话,如果有异常,则回滚所有内容。最好保留这个逻辑,避免在请求过程中手动提交事务。

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

对于异常日志这样的事情,我会考虑设置一个单独的Session,它不受Pyramid的请求/响应周期(没有ZopeTransactionExtension)的约束,然后用它来创建日志记录。您需要在添加日志记录后手动提交事务:

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