Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pyramid (Python): Override default error view

Task: When an error occurs in one of my views I need to show a custom error view. The error type does not matter.

I tried to override all exceptions by registring a view for all exceptions, like this:

<view
    context="Exception"
    renderer="server_error.pt"
    />

It worked good. All exceptions were showing my view. But the problem was that those errors stopped logging. Of cource I can do something like logger.error(traceback) in my error view, but this is a dumb idea.

How can I register a view for all errors without breaking the logging pyramid system.

like image 644
Taras Voinarovskyi Avatar asked Mar 27 '12 12:03

Taras Voinarovskyi


2 Answers

You can log the error in your view via the plain old python logging. Or Set the status on the response to 500 (assuming your view is now return a status of 200 which indicates a successful response ).

Edit: working example

I'm not a logging expert, but I was under the impression that your logging config in development.ini/production.ini would be picked up, the example below seems to prove that but you be the judge.....

changes to log config from default

[formatter_generic]
# format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
format = y u no work??!!?? %(message)s
# End logging configuration

the views

from pyramid.view import view_config
from webob import Response
import logging
log = logging.getLogger(__name__)

@view_config(route_name='home', renderer='templates/mytemplate.pt')
def my_view(request):
    raise ValueError("oops")
    return {'project':'tstLogError'}


@view_config(context=Exception)
def exception_view(context, request):
    log.error("The error was: %s" % context, exc_info=(context))
    return Response(status_int=500, body=str(context))

    from pyramid.view import view_config
    from webob import Response

console output:

serving on http://0.0.0.0:6543
y u no work??!!?? The error was: oops
Traceback (most recent call last):
  File "/home/twillis/projects/TestLogError/local/lib/python2.7/site-packages/pyramid/tweens.py", line 20, in excview_tween
    response = handler(request)
  File "/home/twillis/projects/TestLogError/local/lib/python2.7/site-packages/pyramid/router.py", line 164, in handle_request
    response = view_callable(context, request)
  File "/home/twillis/projects/TestLogError/local/lib/python2.7/site-packages/pyramid/config/views.py", line 333, in rendered_view
    result = view(context, request)
  File "/home/twillis/projects/TestLogError/local/lib/python2.7/site-packages/pyramid/config/views.py", line 471, in _requestonly_view
    response = view(request)
  File "/home/twillis/projects/TestLogError/tstLogError/tstlogerror/views.py", line 8, in my_view
    raise ValueError("oops")
ValueError: oops

browser screenshot

like image 65
Tom Willis Avatar answered Sep 23 '22 03:09

Tom Willis


It is not dumb to log the exception in your own error view (the exception will be available as request.context.) As is always the case with exceptions, you can either do nothing, handle the exception and log it, or re-raise the same exception.

In my setup, an uncaught exception is logged by the WSGI server itself, in this case wsgiref. It's not possible to trigger both the WSGI server's normal response mechanism and its exception logger.

wsgiref's exception logger:

def log_exception(self,exc_info):
    """Log the 'exc_info' tuple in the server log

    Subclasses may override to retarget the output or change its format.
    """
    try:
        from traceback import print_exception
        stderr = self.get_stderr()
        print_exception(
            exc_info[0], exc_info[1], exc_info[2],
            self.traceback_limit, stderr
        )
        stderr.flush()
    finally:
        exc_info = None

One alternative that may be acceptable to you is to display your custom error page at the web server layer instead of at the Pyramid layer.

like image 42
joeforker Avatar answered Sep 23 '22 03:09

joeforker