There are two similar handlers: AgeHandler1 and AgeHandler2. In the first one we simply raise a specific exception to return an error message, in the second - we manually return
For large projects, I would try to abstract from error numbers, specially because the definition of the HTTP status codes are not in your scope. As much I remember, there is at least one pair of status codes with problematic semantic. I don't remember which they where.
But for a larger project I would recommend, that you define your own error categories that you want to support and map those categories to HTTP codes centrally as you need. When you find out later, that you should use a different status code for some error category, you can do it centrally.
Logically I would try to factor out as much knowledge from the specific handling routine as possible. The exception model of course comes here handy, but similar could be reached with a function call for error handling like:
...
if age < 1 or age > 200:
return self.errorResult('Wrong age value.', WRONG_VALUE)
...
or
...
if age < 1 or age > 200:
return self.wrongValue('Wrong age value.')
...
Overwriting write_error works very well. What I do in my projects is I try to catch any 500
status codes. I then send them to myself over slack (my traffic is low enough that the frequency is very low).
Here's the code to extract a clean stack trace from write_error
. Note in this example I also yank out any references to 'gen.py', 'concurrent.py' or 'web.py', which makes for much cleaner stack traces.
import tornado.web, traceback, logging
class MyRequestHandler(tornado.web.RequestHandler):
def write_error(self,status_code,**kwargs):
if status_code == 500:
excp = kwargs['exc_info'][1]
tb = kwargs['exc_info'][2]
stack = traceback.extract_tb(tb)
clean_stack = [i for i in stack if i[0][-6:] != 'gen.py' and i[0][-13:] != 'concurrent.py']
error_msg = '{}\n Exception: {}'.format(''.join(traceback.format_list(clean_stack)),excp)
# do something with this error now... e.g., send it to yourself
# on slack, or log it.
logging.error(error_msg) # do something with your error...
# don't forget to show a user friendly error page!
self.render("oops.html")
The output looks like this:
File "app.py", line 55, in get
assert 1==2,"A fake error to trigger a critical alert."
Exception: A fake error to trigger a critical alert.
In general the best approach is to override RequestHandler.write_error
. This is similar to your first approach, but you don't need the try/except in the body of the handler because Tornado will handle this for you.
Explicit tests like those in your second example are also good, but it's impractical to catch all possible errors this way so you'll always need something to handle uncaught exceptions.