アプリケーションエラー
バージョン0.3の新機能。
アプリケーションは失敗し、サーバーは失敗します。 遅かれ早かれ、本番環境で例外が発生します。 コードが100 % c正しい場合でも、例外が発生することがあります。 どうして? 関係する他のすべてが失敗するからです。 完全に細かいコードがサーバーエラーにつながる可能性があるいくつかの状況を次に示します。
- クライアントはリクエストを早期に終了しましたが、アプリケーションはまだ受信データから読み取っていました
- データベースサーバーが過負荷になり、クエリを処理できませんでした
- ファイルシステムがいっぱいです
- ハードドライブがクラッシュしました
- バックエンドサーバーが過負荷
- 使用しているライブラリのプログラミングエラー
- サーバーの別のシステムへのネットワーク接続に失敗しました
そして、それはあなたが直面する可能性のある問題のほんの一例です。 では、そのような問題にどのように対処するのでしょうか。 デフォルトでは、アプリケーションが本番モードで実行されている場合、Flaskは非常に単純なページを表示し、例外をlogger
に記録します。
しかし、できることは他にもあります。エラーに対処するためのより良いセットアップについて説明します。
エラーログツール
エラーメールの送信は、たとえ重要なものであっても、十分な数のユーザーがエラーに遭遇し、通常はログファイルが表示されない場合、圧倒される可能性があります。 このため、アプリケーションエラーの処理には Sentry の使用をお勧めします。 GitHub でオープンソースプロジェクトとして利用でき、無料で試すことができるホストバージョンとしても利用できます。 Sentryは重複エラーを集約し、デバッグ用に完全なスタックトレースとローカル変数をキャプチャし、新しいエラーまたは頻度のしきい値に基づいてメールを送信します。
Sentryを使用するには、 raven クライアントを追加の flask 依存関係とともにインストールする必要があります。
$ pip install raven[flask]
そして、これをFlaskアプリに追加します。
from raven.contrib.flask import Sentry
sentry = Sentry(app, dsn='YOUR_DSN_HERE')
または、ファクトリを使用している場合は、後で初期化することもできます。
from raven.contrib.flask import Sentry
sentry = Sentry(dsn='YOUR_DSN_HERE')
def create_app():
app = Flask(__name__)
sentry.init_app(app)
...
return app
YOUR_DSN_HERE 値は、Sentryインストールから取得したDSN値に置き換える必要があります。
その後、障害は自動的にSentryに報告され、そこからエラー通知を受け取ることができます。
エラーハンドラ
エラーが発生したときに、カスタムエラーページをユーザーに表示したい場合があります。 これは、エラーハンドラーを登録することで実行できます。
エラーハンドラーは、応答を返す通常のビュー関数ですが、ルートに登録される代わりに、要求の処理中に発生する例外またはHTTPステータスコードに登録されます。
登録
関数をerrorhandler()
で装飾して、ハンドラーを登録します。 または、register_error_handler()
を使用して後で関数を登録します。 応答を返すときは、エラーコードを設定することを忘れないでください。
@app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_bad_request(e):
return 'bad request!', 400
# or, without the decorator
app.register_error_handler(400, handle_bad_request)
werkzeug.exceptions.HTTPException
BadRequest
のようなサブクラスとそれらのHTTPコードは、ハンドラーを登録するときに交換可能です。 (BadRequest.code == 400
)
非標準のHTTPコードは、Werkzeugに認識されていないため、コードで登録できません。 代わりに、適切なコードを使用してHTTPException
のサブクラスを定義し、その例外クラスを登録して発生させます。
class InsufficientStorage(werkzeug.exceptions.HTTPException):
code = 507
description = 'Not enough storage space.'
app.register_error_handler(InsuffcientStorage, handle_507)
raise InsufficientStorage()
ハンドラーは、HTTPException
サブクラスやHTTPステータスコードだけでなく、任意の例外クラスに登録できます。 ハンドラーは、特定のクラス、または親クラスのすべてのサブクラスに登録できます。
取り扱い
リクエストの処理中にFlaskによって例外がキャッチされると、最初にコードによって検索されます。 コードにハンドラーが登録されていない場合は、クラス階層によって検索されます。 最も具体的なハンドラーが選択されます。 ハンドラーが登録されていない場合、HTTPException
サブクラスはコードに関する一般的なメッセージを表示しますが、他の例外は一般的な500内部サーバーエラーに変換されます。
たとえば、ConnectionRefusedError
のインスタンスが発生し、ハンドラーがConnectionError
およびConnectionRefusedError
に登録されている場合、より具体的なConnectionRefusedError
ハンドラーが応答を生成するための例外インスタンス。
ブループリントが例外を発生させる要求を処理していると仮定すると、ブループリントに登録されているハンドラーは、アプリケーションにグローバルに登録されているハンドラーよりも優先されます。 ただし、ブループリントを決定する前に404がルーティングレベルで発生するため、ブループリントは404ルーティングエラーを処理できません。
バージョン0.11で変更:ハンドラーは、登録されている順序ではなく、登録されている例外クラスの特異性によって優先されます。
アプリケーションエラーのデバッグ
本番アプリケーションの場合は、アプリケーションエラーの説明に従って、ログと通知を使用してアプリケーションを構成します。 このセクションでは、デプロイメント構成をデバッグし、フル機能のPythonデバッガーを使用してさらに深く掘り下げる際の指針を示します。
疑わしい場合は、手動で実行する
アプリケーションを本番用に構成する際に問題が発生しましたか? ホストへのシェルアクセスがある場合は、デプロイメント環境のシェルからアプリケーションを手動で実行できることを確認してください。 権限の問題をトラブルシューティングするために、構成されたデプロイメントと同じユーザーアカウントで実行するようにしてください。 本番ホストで debug = True を指定してFlaskの組み込み開発サーバーを使用できます。これは、構成の問題を検出するのに役立ちますが、制御された環境で一時的にこれを実行してください。しないでください debug = True を使用して本番環境で実行します。
デバッガーの操作
より深く掘り下げるために、おそらくコード実行をトレースするために、Flaskは箱から出してすぐにデバッガーを提供します(デバッグモードを参照)。 別のPythonデバッガーを使用する場合は、デバッガーが相互に干渉することに注意してください。 お気に入りのデバッガーを使用するには、いくつかのオプションを設定する必要があります。
debug
-デバッグモードを有効にして例外をキャッチするかどうかuse_debugger
-内部Flaskデバッガーを使用するかどうかuse_reloader
-例外時にプロセスをリロードしてフォークするかどうか
debug
は、他の2つのオプションに任意の値を設定するために、Trueである必要があります(つまり、例外をキャッチする必要があります)。
デバッグにAptana / Eclipseを使用している場合は、use_debugger
とuse_reloader
の両方をFalseに設定する必要があります。
構成に役立つ可能性のあるパターンは、config.yamlで次のように設定することです(もちろん、アプリケーションに応じてブロックを変更してください)。
FLASK:
DEBUG: True
DEBUG_WITH_APTANA: True
次に、アプリケーションのエントリポイント(main.py)に、次のようなものを含めることができます。
if __name__ == "__main__":
# To allow aptana to receive errors, set use_debugger=False
app = create_app(config="config.yaml")
if app.debug: use_debugger = True
try:
# Disable Flask's debugger if external debugger is requested
use_debugger = not(app.config.get('DEBUG_WITH_APTANA'))
except:
pass
app.run(use_debugger=use_debugger, debug=app.debug,
use_reloader=use_debugger, host='0.0.0.0')