Merge pull request #291 from subyraman/master
Add rich HTML traceback in debug mode, add HTML 500 page in prod
This commit is contained in:
commit
9108a4c69f
|
@ -12,3 +12,4 @@ kyoukai
|
|||
falcon
|
||||
tornado
|
||||
aiofiles
|
||||
beautifulsoup4
|
||||
|
|
|
@ -1,6 +1,104 @@
|
|||
from .response import text
|
||||
from .response import text, html
|
||||
from .log import log
|
||||
from traceback import format_exc
|
||||
from traceback import format_exc, extract_tb
|
||||
import sys
|
||||
|
||||
TRACEBACK_STYLE = '''
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.summary {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
h3 code {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.frame-line > * {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.frame-line {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.frame-code {
|
||||
font-size: 16px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.tb-wrapper {
|
||||
border: 1px solid #f3f3f3;
|
||||
}
|
||||
|
||||
.tb-header {
|
||||
background-color: #f3f3f3;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.frame-descriptor {
|
||||
background-color: #e2eafb;
|
||||
}
|
||||
|
||||
.frame-descriptor {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
'''
|
||||
|
||||
TRACEBACK_WRAPPER_HTML = '''
|
||||
<html>
|
||||
<head>
|
||||
{style}
|
||||
</head>
|
||||
<body>
|
||||
<h1>{exc_name}</h1>
|
||||
<h3><code>{exc_value}</code></h3>
|
||||
<div class="tb-wrapper">
|
||||
<p class="tb-header">Traceback (most recent call last):</p>
|
||||
{frame_html}
|
||||
<p class="summary">
|
||||
<b>{exc_name}: {exc_value}</b>
|
||||
while handling uri <code>{uri}</code>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
TRACEBACK_LINE_HTML = '''
|
||||
<div class="frame-line">
|
||||
<p class="frame-descriptor">
|
||||
File {0.filename}, line <i>{0.lineno}</i>,
|
||||
in <code><b>{0.name}</b></code>
|
||||
</p>
|
||||
<p class="frame-code"><code>{0.line}</code></p>
|
||||
</div>
|
||||
'''
|
||||
|
||||
INTERNAL_SERVER_ERROR_HTML = '''
|
||||
<h1>Internal Server Error</h1>
|
||||
<p>
|
||||
The server encountered an internal error and cannot complete
|
||||
your request.
|
||||
</p>
|
||||
'''
|
||||
|
||||
|
||||
class SanicException(Exception):
|
||||
|
@ -46,6 +144,21 @@ class Handler:
|
|||
self.handlers = {}
|
||||
self.sanic = sanic
|
||||
|
||||
def _render_traceback_html(self, exception, request):
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
frames = extract_tb(tb)
|
||||
|
||||
frame_html = []
|
||||
for frame in frames:
|
||||
frame_html.append(TRACEBACK_LINE_HTML.format(frame))
|
||||
|
||||
return TRACEBACK_WRAPPER_HTML.format(
|
||||
style=TRACEBACK_STYLE,
|
||||
exc_name=exc_type.__name__,
|
||||
exc_value=exc_value,
|
||||
frame_html=''.join(frame_html),
|
||||
uri=request.url)
|
||||
|
||||
def add(self, exception, handler):
|
||||
self.handlers[exception] = handler
|
||||
|
||||
|
@ -77,11 +190,12 @@ class Handler:
|
|||
'Error: {}'.format(exception),
|
||||
status=getattr(exception, 'status_code', 500))
|
||||
elif self.sanic.debug:
|
||||
html_output = self._render_traceback_html(exception, request)
|
||||
|
||||
response_message = (
|
||||
'Exception occurred while handling uri: "{}"\n{}'.format(
|
||||
request.url, format_exc()))
|
||||
log.error(response_message)
|
||||
return text(response_message, status=500)
|
||||
return html(html_output, status=500)
|
||||
else:
|
||||
return text(
|
||||
'An error occurred while generating the response', status=500)
|
||||
return html(INTERNAL_SERVER_ERROR_HTML, status=500)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import pytest
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import text
|
||||
|
@ -75,7 +76,13 @@ def test_handled_unhandled_exception(exception_app):
|
|||
request, response = sanic_endpoint_test(
|
||||
exception_app, uri='/divide_by_zero')
|
||||
assert response.status == 500
|
||||
assert response.body == b'An error occurred while generating the response'
|
||||
soup = BeautifulSoup(response.body, 'html.parser')
|
||||
assert soup.h1.text == 'Internal Server Error'
|
||||
|
||||
message = " ".join(soup.p.text.split())
|
||||
assert message == (
|
||||
"The server encountered an internal error and "
|
||||
"cannot complete your request.")
|
||||
|
||||
|
||||
def test_exception_in_exception_handler(exception_app):
|
||||
|
|
|
@ -2,6 +2,7 @@ from sanic import Sanic
|
|||
from sanic.response import text
|
||||
from sanic.exceptions import InvalidUsage, ServerError, NotFound
|
||||
from sanic.utils import sanic_endpoint_test
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
exception_handler_app = Sanic('test_exception_handler')
|
||||
|
||||
|
@ -21,6 +22,12 @@ def handler_3(request):
|
|||
raise NotFound("OK")
|
||||
|
||||
|
||||
@exception_handler_app.route('/4')
|
||||
def handler_4(request):
|
||||
foo = bar
|
||||
return text(foo)
|
||||
|
||||
|
||||
@exception_handler_app.exception(NotFound, ServerError)
|
||||
def handler_exception(request, exception):
|
||||
return text("OK")
|
||||
|
@ -47,3 +54,20 @@ def test_text_exception__handler():
|
|||
exception_handler_app, uri='/random')
|
||||
assert response.status == 200
|
||||
assert response.text == 'OK'
|
||||
|
||||
|
||||
def test_html_traceback_output_in_debug_mode():
|
||||
request, response = sanic_endpoint_test(
|
||||
exception_handler_app, uri='/4', debug=True)
|
||||
assert response.status == 500
|
||||
soup = BeautifulSoup(response.body, 'html.parser')
|
||||
html = str(soup)
|
||||
|
||||
assert 'response = handler(request, *args, **kwargs)' in html
|
||||
assert 'handler_4' in html
|
||||
assert 'foo = bar' in html
|
||||
|
||||
summary_text = " ".join(soup.select('.summary')[0].text.split())
|
||||
assert (
|
||||
"NameError: name 'bar' "
|
||||
"is not defined while handling uri /4") == summary_text
|
||||
|
|
Loading…
Reference in New Issue
Block a user