After spending a nontrivial amount of my nights and weekends working on an App Engine app, I wanted a good way to monitor the logs without checking in on them every day. After a particularly frustrating weekend of updates that exposed unnoticed bugs that had yet to be triggered by the app, I set out to find such a way. I set out to find a Pythonic way.
Since I knew the
App Engine Mail API was
super easy to configure, I figured I would just
email myself every time there was an exception, before serving my
default 500 error page. To do so, I just needed to subclass the default
RequestHandler
with my own
handle_exception
method. (OK, prepare yourselves,
a bunch of code is about to happen. See the necessary
imports at the bottom of the post.)
class ExtendedHandler(RequestHandler):
def handle_exception(self, exception, debug_mode):
traceback_info = ''.join(format_exception(*sys.exc_info()))
email_admins(traceback_info, defer_now=True)
serve_500(self)
Awesome! By making all my handlers inherit from ExtendedHandler
,
I can use the native Python modules traceback
and sys
to get the traceback and my handy dandy
def email_admins(error_msg, defer_now=False):
if defer_now:
defer(email_admins, error_msg, defer_now=False)
return
sender = 'YOUR APP Errors <errors@your_app_id_here.appspotmail.com>'
to = 'Robert Admin <[email protected]>, James Nekbehrd <[email protected]>'
subject = 'YOUR APP Error: Admin Notify'
body = '\n'.join(['Dearest Admin,',
'',
'An error has occurred in YOUR APP:',
error_msg,
''])
mail.send_mail(sender=sender, to=to,
subject=subject, body=body)
to send out the email in the deferred queue as not to hold up the handler serving the page. Mission accomplished, right? WRONG!
Unfortunately, handle_exception
only handles
the "right" kind of exceptions. That is, exceptions which inherit
directly from Python's Exception
.
From the horse's
mouth:
Exceptions should typically be derived from the
Exception
class, either directly or indirectly.
But. But! If the app fails
because a request times out, a DeadlineExceededError
is thrown and
handle_exception
falls on its face. Why? Because DeadlineExceededError
inherits
directly from Exception
's parent class:BaseException
.
It's OK little ones, in my next post I explain how I did it while keeping my code Pythonic by using metaclasses.
Imports:
from google.appengine.api import mail
from google.appengine.ext.deferred import defer
from google.appengine.ext.webapp import RequestHandler
import sysfrom traceback import format_exception
from SOME_APP_SPECIFIC_LIBRARY import serve_500
Pythonic:
An idea or piece of code which closely follows the most common idioms of the Python language, rather than implementing code using concepts common to other languages.
Deferred Queue:
Make sure to enable the deferred library in your app.yaml
by using deferred: on
in your builtins.