61

うまく動作し、時折エラーを生成するFlaskアプリケーションがあります。これは、次のように実行すると表示されますdebug=True

if __name__ == '__main__':
    app.run(debug=True)

次のような有用なエラー メッセージが表示されます。

Traceback (most recent call last):
  File "./main.py", line 871, in index_route

KeyError: 'stateIIIII'

(Lighttpd + fastcgi を使用して) 実稼働環境でアプリケーションを実行すると、これらのようなエラー メッセージがファイルに保存されます。

さまざまな StackOverflow の質問 ( http://flask.pocoo.org/docs/errorhandling/http://docs.python.org/2/library/logging.htmlなど) を見た後; Flask メーリング リスト。いくつかのブログでは、すばらしいエラー メッセージをすべてファイルに送信するだけの簡単な方法はないようです。Python ロギング モジュールを使用してカスタマイズする必要があります。そこで、次のコードを思いつきました。

私のアプリケーションファイルの上部には、さまざまなインポートがあり、その後に次のものが続きます。

app = Flask(__name__)

if app.debug is not True:   
    import logging
    from logging.handlers import RotatingFileHandler
    file_handler = RotatingFileHandler('python.log', maxBytes=1024 * 1024 * 100, backupCount=20)
    file_handler.setLevel(logging.ERROR)
    app.logger.setLevel(logging.ERROR)
    app.logger.addHandler(file_handler)

次に、各ルートのコードを try/except ステートメントに入れ、トレースバックを使用してエラーが発生した行を特定し、適切なエラー メッセージを出力します。

def some_route():
    try:
        # code for route in here (including a return statement)

    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        app.logger.error(traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2))
        return render_template('error.html')

そして、ファイルの最後でdebug=Trueステートメントを削除します。アプリケーションが本番環境で実行されている場合、アプリケーションは fastcgi サーバー (?) によって実行されているため、それを行う必要はないと思いますが。アプリケーション コードの最後の 2 行は次のようになります。

if __name__ == '__main__':
    app.run()

私はこれを機能させるのに苦労しています。私が管理した最善の方法は、( app.logger.error('test message')) を使用して単一のエラー ログ メッセージをファイルに保存することだと思いますが、その 1 つのメッセージしか出力しません。そのエラーの直後に別のエラーをログに記録しようとしても、単に無視されます。

4

5 に答える 5

69

なぜ機能しないのかわかりませんが、これがどのように行われているかはわかります。

まず、app.logger のレベルを設定する必要はありません。したがって、この行を削除しますapp.logger.setLevel()

ビューごとに例外を保存し、エラー ページを返します。このコードをどこにでも書くのは大変な作業です。Flask はこれを行う方法を提供します。このように errorhandler メソッドを定義します。

    @app.errorhandler(500)
    def internal_error(exception):
        app.logger.error(exception)
        return render_template('500.html'), 500

ビューが例外を発生させるたびに、このメソッドが呼び出され、例外が引数として渡されます。Python ロギングは、例外の完全なトレースバックを保存するために使用される例外メソッドを提供します。

これはすべての例外を処理するため、コードを try/except ブロックに入れる必要さえありません。ただし、エラーハンドラーを呼び出す前に何かをしたい場合 (ロールバック セッションやトランザクションなど) は、次のようにします。

    try:
        #code
    except:
        #code
        raise

ログ ファイルの各エントリに日付と時刻を追加する場合は、次のコードを使用できます (質問で紹介されている同様のコードの代わりに)。

if app.debug is not True:   
    import logging
    from logging.handlers import RotatingFileHandler
    file_handler = RotatingFileHandler('python.log', maxBytes=1024 * 1024 * 100, backupCount=20)
    file_handler.setLevel(logging.ERROR)
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    file_handler.setFormatter(formatter)
    app.logger.addHandler(file_handler)
于 2012-12-26T15:36:32.950 に答える
37

これから読む方へ。

より有用な情報をエラー メッセージにプッシュする方がよいと思います。URL、クライアント IP、ユーザー エージェントなど。Flask は関数を使用して内部的に (app.debug==Falseモードで) 例外をログに記録しFlask.log_exceptionます。したがって、手動でログに記録する代わりに、次の@app.errorhandlerようにします。

class MoarFlask(Flask):
    def log_exception(self, exc_info):
        """...description omitted..."""
        self.logger.error(
            """
Request:   {method} {path}
IP:        {ip}
User:      {user}
Agent:     {agent_platform} | {agent_browser} {agent_browser_version}
Raw Agent: {agent}
            """.format(
                method = request.method,
                path = request.path,
                ip = request.remote_addr,
                agent_platform = request.user_agent.platform,
                agent_browser = request.user_agent.browser,
                agent_browser_version = request.user_agent.version,
                agent = request.user_agent.string,
                user=user
            ), exc_info=exc_info
        )

次に、構成時にバインドFileHandlerapp.loggerて続行します。私はStreamHandler、多くのサーバー (uWSGI など) が独自の独自の言葉遣いの役に立たないオフにできないメッセージで汚染することを好むため、使用しません。

Flask の拡張を恐れないでください。あなたは遅かれ早かれそれをすることを余儀なくされるでしょう;)

于 2013-12-13T15:26:41.940 に答える