Ben*_*Ben 57 python logging flask
我有一个Flask应用程序运行良好并产生偶然的错误,当它运行时可见debug=True
:
if __name__ == '__main__':
app.run(debug=True)
Run Code Online (Sandbox Code Playgroud)
我收到有用的错误消息,例如:
Traceback (most recent call last):
File "./main.py", line 871, in index_route
KeyError: 'stateIIIII'
Run Code Online (Sandbox Code Playgroud)
我想在生产中运行应用程序(使用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)
Run Code Online (Sandbox Code Playgroud)
然后我将每个路由的代码放在try/except语句中,并使用traceback来确定错误来自哪一行并打印一条漂亮的错误消息:
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')
Run Code Online (Sandbox Code Playgroud)
然后在文件的末尾我删除该debug=True
语句.虽然我不认为我需要这样做,因为当它在生产中运行时,应用程序由fastcgi服务器(?)运行.我的应用程序代码的最后两行如下所示:
if __name__ == '__main__':
app.run()
Run Code Online (Sandbox Code Playgroud)
我正在努力让这个工作.我认为我所管理的最好的是使用(app.logger.error('test message')
)将单个错误日志消息保存在文件中,但它只打印一条消息.简单地忽略在该那个之后直接记录另一个错误的尝试.
cod*_*ool 60
我不知道为什么它不起作用,但我可以告诉我这是怎么做的.
首先,您不需要设置app.logger的级别.所以删除这一行app.logger.setLevel()
.
您希望为每个视图保存异常并返回错误页面.在任何地方编写此代码都需要做很多工作.Flask提供了一种方法.定义像这样的错误处理程序方法.
@app.errorhandler(500)
def internal_error(exception):
app.logger.error(exception)
return render_template('500.html'), 500
Run Code Online (Sandbox Code Playgroud)
每当视图引发异常时,将调用此方法并将异常作为参数传递.Python日志记录提供了异常方法,用于保存异常的完整回溯.
由于这会处理所有异常,因此您甚至不需要将代码放在try/except块中.但是,如果您想在调用错误处理程序之前执行某些操作(例如回滚会话或事务),请执行以下操作:
try:
#code
except:
#code
raise
Run Code Online (Sandbox Code Playgroud)
如果您希望为日志文件中的每个条目添加日期和时间,可以使用以下代码(代替问题中的类似代码).
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)
Run Code Online (Sandbox Code Playgroud)
Iva*_*nin 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
)
Run Code Online (Sandbox Code Playgroud)
然后,在配置时,绑定FileHandler
到app.logger
和继续.我没有使用StreamHandler
因为许多服务器(例如uWSGI)喜欢使用他们自己专有的无用 - 无用 - 不可关闭的消息来污染它.
不要害怕延长Flask.你迟早会被迫这样做;)
iva*_*ncz 10
我不是logging
模块的专家,但根据我的经验+在Python + Flask上的几年,你可以有一个很好的日志配置,考虑一些观察:
在每个函数(路由)的开头,创建一个时间戳对象,以便在发出请求时注册确切的时间,如果成功则不成功
使用@ 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 __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)
Run Code Online (Sandbox Code Playgroud)
有关logrotate和stdout和文件同时登录的更多信息:这个要点