ErrorWriter: Add support for format_traceback.

This commit is contained in:
Dave Shawley 2015-11-20 07:25:05 -05:00
parent cdb655be1e
commit a7c8d95847
2 changed files with 39 additions and 9 deletions

View file

@ -8,6 +8,7 @@ HTTP related utility mixins.
"""
import logging
import json
import traceback
from tornado import httputil
@ -79,7 +80,7 @@ class ErrorWriter(object):
Mix this class in to your inheritance chain to include error
bodies as a standard JSON document. The error document has
two simple properties:
three simple properties:
**type**
This is the type of exception that occurred or ``null``.
@ -98,18 +99,27 @@ class ErrorWriter(object):
neither an exception nor a custom reason, the string ``Unknown``
will be used.
**traceback**
If the application is configured to serve tracebacks and the
error was caused by an exception (based on ``exc_info`` kwarg),
then this is the formatted traceback as an array of strings
returned from :func:`traceback.format_exception`. Otherwise,
this property is set to ``null``.
"""
def write_error(self, status_code, **kwargs):
error_body = {'type': None}
error_body = {'type': None, 'traceback': None}
exc_type, exc_value, _ = kwargs.get('exc_info', (None, None, None))
if exc_type and exc_value:
error_body['type'] = exc_type.__name__
error_body.setdefault('message', str(exc_value))
if self.settings.get('serve_traceback', False):
error_body['traceback'] = traceback.format_exception(
*kwargs['exc_info'])
else:
error_body.setdefault('message',
kwargs.get('reason',
_get_http_reason(status_code)))
reason = kwargs.get('reason', _get_http_reason(status_code))
error_body.setdefault('message', reason)
self.set_header('Content-Type', 'application/json')
self.write(json.dumps(error_body).encode('utf-8'))

View file

@ -86,11 +86,22 @@ class ErrorLoggerTests(testing.AsyncHTTPTestCase):
class ErrorWriterTests(testing.AsyncHTTPTestCase):
def get_app(self):
return web.Application([
web.url(r'/status/(?P<status_code>\d+)', examples.StatusHandler),
def setUp(self):
self._application = None
super(ErrorWriterTests, self).setUp()
@property
def application(self):
if self._application is None:
self._application = web.Application([
web.url(r'/status/(?P<status_code>\d+)',
examples.StatusHandler),
web.url(r'/fail/(?P<status_code>\d+)', RaisingHandler),
])
return self._application
def get_app(self):
return self.application
def _decode_response(self, response):
content_type = response.headers['Content-Type']
@ -141,3 +152,12 @@ class ErrorWriterTests(testing.AsyncHTTPTestCase):
body = self._decode_response(response)
self.assertEqual(body['message'], 'Unknown')
def test_that_error_json_honors_serve_traceback(self):
self.application.settings['serve_traceback'] = True
response = self.fetch('/fail/400')
self.assertEqual(response.code, 400)
body = self._decode_response(response)
self.assertGreater(len(body['traceback']), 0)