Flaskアプリケーションは正常に動作し、時折エラーを生成します。これは、debug=True
で実行しているときに表示されます。
if __== '__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ステートメントに入れ、トレースバックを使用してエラーの原因となった行を特定し、Niceエラーメッセージを出力しました。
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 __== '__main__':
app.run()
これを機能させるのに苦労しています。私が管理した最善の方法は、(app.logger.error('test message')
)を使用してファイルに保存される単一のエラーログメッセージを取得することですが、それはその1つのメッセージのみを出力します。そのエラーの直後に別のエラーを記録しようとしても、無視されます。
なぜ機能しないのかはわかりませんが、これがどのように行われているかはわかります。
まず、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)
後でこれを読む人のために。
より有用な情報をエラーメッセージにプッシュすることをお勧めします。 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
)
次に、構成時に、FileHandler
をapp.logger
にバインドして続行します。私はStreamHandler
を使用していないので、多くのサーバー(uWSGIなど)が独自の独自の単語を使い物にならない、回転不可能なメッセージで汚染することを好みます。
Flaskを拡張することを恐れないでください。あなたは遅かれ早かれそれを行うことを余儀なくされます;)
私はlogging
モジュールの専門家ではありませんが、その経験+ Python + Flaskでの経験については、いくつかの観察を考慮して、適切なログ設定を行うことができます:
すべての機能(ルート)の先頭で、timestampオブジェクトを作成し、リクエストが行われた正確な時間を登録します成功したかどうか
@ app.after_requestを使用して、成功したすべてのリクエストを登録します
@ app.errorhandlerを使用して、一般的なエラー+トレースバックを登録します。
このアイデアを示す例を次に示します。
#/usr/bin/python3
""" Demonstration of logging feature for a Flask App. """
from logging.handlers import RotatingFileHandler
from flask import Flask, request, jsonify
from time import strftime
__author__ = "@ivanleoncz"
import logging
import traceback
app = Flask(__name__)
@app.route("/")
@app.route("/index")
def get_index():
""" Function for / and /index routes. """
return "Welcome to Flask! "
@app.route("/data")
def get_data():
""" Function for /data route. """
data = {
"Name":"Ivan Leon",
"Occupation":"Software Developer",
"Technologies":"[Python, Flask, JavaScript, Java, SQL]"
}
return jsonify(data)
@app.route("/error")
def get_nothing():
""" Route for intentional error. """
return foobar # intentional non-existent variable
@app.after_request
def after_request(response):
""" Logging after every request. """
# This avoids the duplication of registry in the log,
# since that 500 is already logged via @app.errorhandler.
if response.status_code != 500:
ts = strftime('[%Y-%b-%d %H:%M]')
logger.error('%s %s %s %s %s %s',
ts,
request.remote_addr,
request.method,
request.scheme,
request.full_path,
response.status)
return response
@app.errorhandler(Exception)
def exceptions(e):
""" Logging after every Exception. """
ts = strftime('[%Y-%b-%d %H:%M]')
tb = traceback.format_exc()
logger.error('%s %s %s %s %s 5xx INTERNAL SERVER ERROR\n%s',
ts,
request.remote_addr,
request.method,
request.scheme,
request.full_path,
tb)
return "Internal Server Error", 500
if __== '__main__':
handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=3)
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
logger.addHandler(handler)
app.run(Host="127.0.0.1",port=8000)
Logrotateとstdoutとfileの同時ログオンに関する詳細については、 this Gist
Gunicornを使用してFlaskアプリを実行している場合、gunicornエラーハンドラーをFlaskロガーに追加することで、すべてのFlask例外をgunicornログに記録できます。
module/__init__.py
:
@app.before_first_request
def setup_logging():
if not app.debug:
import logging
gunicorn_logger = logging.getLogger('gunicorn.error')
for handler in gunicorn_logger.handlers:
app.logger.addHandler(handler)