Python 如何将 Flask 出色的调试日志消息写入生产中的文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14037975/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I write Flask's excellent debug log message to a file in production?
提问by Ben
I have a Flask application that works well and produces an occasional error, which is visible when it is running with debug=True:
我有一个运行良好的 Flask 应用程序,偶尔会产生一个错误,当它运行时可见debug=True:
if __name__ == '__main__':
app.run(debug=True)
I get useful error messages such as:
我收到有用的错误消息,例如:
Traceback (most recent call last):
File "./main.py", line 871, in index_route
KeyError: 'stateIIIII'
I would like to get error messages like these saved to a file when I run the application in production (using Lighttpd + fastcgi).
当我在生产环境中运行应用程序(使用 Lighttpd + fastcgi)时,我希望将这些错误消息保存到文件中。
After looking at various StackOverflow questions (http://flask.pocoo.org/docs/errorhandling/, http://docs.python.org/2/library/logging.html, etc.); the Flask mailing list; and a few blogs, it seems there is no easy way just to send all the great error messages to a file - I need to use the Python logging module to customise things. So I came up with the following code.
在查看了各种 StackOverflow 问题(http://flask.pocoo.org/docs/errorhandling/、http://docs.python.org/2/library/logging.html等)之后;Flask 邮件列表;和一些博客,似乎没有简单的方法将所有重要的错误消息发送到文件 - 我需要使用 Python 日志记录模块来自定义内容。所以我想出了以下代码。
At the top of my application file I have various imports followed by:
在我的应用程序文件的顶部,我有各种导入,然后是:
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)
I have then put the code for each route in a try/except statement and use traceback to work out which line the error came from and print a nice error message:
然后,我将每个路由的代码放在 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')
And then right at the end of the file I remove the debug=Truestatement. Though I don't think I need to do that as the application is being run by a fastcgi server(?) when it is run in production. The last two lines of my application code look like this:
然后就在文件的末尾,我删除了该debug=True语句。虽然我认为我不需要这样做,因为应用程序在生产中运行时由 fastcgi 服务器(?)运行。我的应用程序代码的最后两行如下所示:
if __name__ == '__main__':
app.run()
I am struggling to get this working. I think the best I have managed is to get a single error log message to be saved in the file using (app.logger.error('test message')), but it only prints that one message. An attempt to log another error directly after that one is simply ignored.
我正在努力让这个工作。我认为我管理的最好的方法是使用 ( app.logger.error('test message'))将一条错误日志消息保存在文件中,但它只打印一条消息。在该错误之后直接记录另一个错误的尝试被简单地忽略。
采纳答案by codecool
I don't know why it's not working but I can tell how am doing this.
我不知道为什么它不起作用,但我可以告诉我是如何做到这一点的。
First of all, you don't need to set the level of app.logger. So remove this line app.logger.setLevel().
首先,你不需要设置app.logger的级别。所以删除这一行app.logger.setLevel()。
You want to save exception and return error page for every view. It is a lot of work to write this code everywhere. Flask provides a method to do this. Define an errorhandler method like this.
您想为每个视图保存异常并返回错误页面。到处编写这段代码需要大量的工作。Flask 提供了一种方法来做到这一点。像这样定义一个错误处理程序方法。
@app.errorhandler(500)
def internal_error(exception):
app.logger.error(exception)
return render_template('500.html'), 500
Whenever a view raises an exception, this method will be called and passed the exception as argument. Python logging provides exception method that is used to save full traceback of the exception.
每当视图引发异常时,都会调用此方法并将异常作为参数传递。Python logging 提供了异常方法,用于保存异常的完整回溯。
Since this handles all exception, you don't even need to put code in try/except block. Though, if you want to do something before calling the errorhandler(for e.g. rollback session or transaction) then do this:
由于这会处理所有异常,因此您甚至不需要将代码放入 try/except 块中。但是,如果您想在调用错误处理程序之前做一些事情(例如回滚会话或事务),请执行以下操作:
try:
#code
except:
#code
raise
If you would like the date and time added for each entry in your log file, the following code can be used (in place of the similar code featured in the question).
如果您希望为日志文件中的每个条目添加日期和时间,可以使用以下代码(代替问题中的类似代码)。
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)
回答by Ivan Kleshnin
For those who read this later on.
对于那些稍后阅读本文的人。
I think it is better idea to push more useful info into error messages. URL, client IP, user-agent etc. Flask logs exceptions internally (in app.debug==Falsemode) with Flask.log_exceptionfunction. So, instead of logging things manually in @app.errorhandlerI do something like this:
我认为将更多有用的信息推送到错误消息中是更好的主意。URL、客户端 IP、用户代理等。 Flaskapp.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
)
Then, at configuration time, bind FileHandlerto app.loggerand go on.
I don't use StreamHandlercause many servers (e.g. uWSGI) like to pollute it
with their own proprietary-wordy-useless-not-turnable-off messages.
然后,在配置时,绑定FileHandler到app.logger并继续。我不使用StreamHandler因为许多服务器(例如 uWSGI)喜欢用他们自己专有的冗长无用的不可关闭消息来污染它。
Don't be afraid of extending Flask. You'll be forced to do it sooner or later ;)
不要害怕扩展 Flask。你迟早会被迫这样做;)
回答by ivanleoncz
I'm not a specialist on loggingmodule, but regarding my experience on it + some years of on Python + Flask, you can have a good logging configuration, considering some observations:
我不是logging模块方面的专家,但根据我在模块方面的经验 + 在 Python + Flask 上的多年经验,考虑到一些观察结果,您可以有一个很好的日志记录配置:
at the beginning of every function (route), create a timestampobject, in order to registry the exact time when the request was made, independently if it was successful or not
use @app.after_request, for registering every successful request
use @app.errorhandler, for registering general errors + Tracebacks
在每个函数(路由)的开始,创建一个时间戳对象,以便注册请求的确切时间,独立于它是否成功
使用@app.after_request,用于注册每个成功的请求
使用@app.errorhandler,用于注册一般错误 + 回溯
Here is an example that demonstrates this idea:
下面是一个演示这个想法的例子:
#/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 __name__ == '__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)
For more information regarding logrotate and logs on stdout and file at the same time: this Gist
有关 logrotate 和同时登录 stdout 和文件的更多信息:此 Gist
回答by user545424
If you are using gunicorn to run your Flask app, you can log all Flask exceptions to the gunicorn logs by adding the gunicorn error handlers to the Flask logger:
如果您使用 gunicorn 运行 Flask 应用程序,则可以通过将 gunicorn 错误处理程序添加到 Flask 记录器来将所有 Flask 异常记录到 gunicorn 日志中:
In module/__init__.py:
在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)

