Handle errors in a tornado application, the right way

Exceptions... allow error handling to be organized cleanly in a central or high-level place within the program structure.

Doug Hellmann, Python Exception Handling Techniques

After a few false steps, seems like, I found the right way to handle errors in a tornado application.

Main points:

See my question on stackoverflow.

import json
import traceback

from tornado import web, options, ioloop


class MyAppException(web.HTTPError):

    pass


class MyAppBaseHandler(web.RequestHandler):

    def write_error(self, status_code, **kwargs):

        self.set_header('Content-Type', 'application/json')
        if self.settings.get("serve_traceback") and "exc_info" in kwargs:
            # in debug mode, try to send a traceback
            lines = []
            for line in traceback.format_exception(*kwargs["exc_info"]):
                lines.append(line)
            self.finish(json.dumps({
                'error': {
                    'code': status_code,
                    'message': self._reason,
                    'traceback': lines,
                }
            }))
        else:
            self.finish(json.dumps({
                'error': {
                    'code': status_code,
                    'message': self._reason,
                }
            }))


class AgeHandler(MyAppBaseHandler):

    def get(self):
        age = self.get_argument('age')
        age = int(age)
        if age < 1 or age > 200:
            raise MyAppException(reason='Wrong age value.', status_code=400)
        self.write('Your age is {age}'.format(age=age))


class MyApplication(web.Application):

    def __init__(self, **kwargs):
        kwargs['handlers'] = [
            web.url(r'/', AgeHandler, name='age'),
        ]
        kwargs['debug'] = True
        super(MyApplication, self).__init__(**kwargs)


if __name__ == '__main__':
    options.parse_command_line()
    application = MyApplication()
    application.listen(5000)
    ioloop.IOLoop.instance().start()

debug == False:

{
   "error":{
      "code":400,
      "message":"Wrong age value."
   }
}

debug == True:

{
   "error":{
      "traceback":[
         "Traceback (most recent call last):\n",
         "  File \".env/lib/python3.3/site-packages/tornado/web.py\", line 1332, in _execute\n    result = method(*self.path_args, **self.path_kwargs)\n",
         "  File \"app.py\", line 46, in get\n    raise MyAppException(reason='Wrong age value.', status_code=400)\n",
         "MyAppException: HTTP 400: Wrong age value.\n"
      ],
      "message":"Wrong age value.",
      "code":400
   }
}
Licensed under CC BY-SA 3.0