google app engineでInternal server error 500の動作を独自に定義

google app engine では、webapp.RequestHandler の継承クラスを定義することでHTTPリクエストのハンドラを定義します。例外(Internal server error 500 に相当)が発生した場合の動作を、handle_exception メソッドをオーバーライドすることで独自に定義できます。

サンプル

http://code.google.com/intl/ja/appengine/docs/python/gettingstarted/usingwebapp.htmlのコードがベース。

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

import cgi
import logging
import sys
import traceback

class MainPage(webapp.RequestHandler):
  def get(self):
    # test
    raise RuntimeError('runtime error')

    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, webapp World!')
    
  def handle_exception(self, exception, debug_mode):
    self.error(500)
    logging.exception(exception)
    if debug_mode:
      lines = ''.join(traceback.format_exception(*sys.exc_info()))
      self.response.clear()
      self.response.out.write('<pre>%s</pre>' % (cgi.escape(lines, quote=True)))
    else:
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write('Internal Server Error 500')
          
application = webapp.WSGIApplication(
                                     [('/', MainPage)],
                                     debug=False)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

debug=Trueのときは、ベースコードと全く同じように動作し、debug=Falseのときは、Internal Server Error 500 をブラウザに表示する点がベースコードの場合と異なる。

ベースコードからの変更点は、

  • handle_exceptionメソッドのオーバーライド
  • handle_exceptionメソッドのオーバーライドに伴う追加のimport
  • debug=False

getメソッドの raise RuntimeError('runtime error') はテスト用に挿入。本来不要。

RequestHandler クラス

RequestHandler クラスの handle_exception メソッドと error メソッド。error は handle_exception から呼び出される。

  def handle_exception(self, exception, debug_mode):
    """Called if this handler throws an exception during execution.

    The default behavior is to call self.error(500) and print a stack trace
    if debug_mode is True.

    Args:
      exception: the exception that was thrown
      debug_mode: True if the web application is running in debug mode
    """
    self.error(500)
    logging.exception(exception)
    if debug_mode:
      lines = ''.join(traceback.format_exception(*sys.exc_info()))
      self.response.clear()
      self.response.out.write('<pre>%s</pre>' % (cgi.escape(lines, quote=True)))
  def error(self, code):
    """Clears the response output stream and sets the given HTTP error code.

    Args:
      code: the HTTP status error code (e.g., 501)
    """
    self.response.set_status(code)
    self.response.clear()

handle_exception では、ステータスコードを 500 にセットし、ログをはいている。そして、デバッグモードであれば、トレース情報を出力。

で、どんな感じで作ればいいか

我々は、RequestHandler の継承クラスで handle_exception をオーバーライドするわけだが、RequestHandler の handle_exception を参考にできる。継承クラスにおける handle_exception の処理の流れとしては、errorメソッドでステータスコードをセットし、ログをはく。その後、デバッグモードの場合の処理と、そうでない場合の処理を記述すればよい。