Python Flask 日志记录 - 无法将其写入文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17743019/
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
Flask logging - Cannot get it to write to a file
提问by fleshgolem
Ok, here's the code where I setup everything:
好的,这是我设置所有内容的代码:
if __name__ == '__main__':
app.debug = False
applogger = app.logger
file_handler = FileHandler("error.log")
file_handler.setLevel(logging.DEBUG)
applogger.setLevel(logging.DEBUG)
applogger.addHandler(file_handler)
app.run(host='0.0.0.0')
What happens is
发生的事情是
- error.log gets created
- Nothing is ever written to it
- Despite not adding a StreamHandler and setting debug to false I still get everything to STDOUT (this might be correct, but still seems weird)
- error.log 被创建
- 什么都没有写到它
- 尽管没有添加 StreamHandler 并将调试设置为 false,我仍然将所有内容都设置为 STDOUT(这可能是正确的,但仍然看起来很奇怪)
Am I totally off here somewhere or what is happening?
我是不是完全不在某个地方,或者发生了什么?
采纳答案by HolgerSchurig
Why not do it like this:
为什么不这样做:
if __name__ == '__main__':
init_db() # or whatever you need to do
import logging
logging.basicConfig(filename='error.log',level=logging.DEBUG)
app.run(host="0.0.0.0")
If you now start you application, you'll see that error.log contains:
如果你现在启动你的应用程序,你会看到 error.log 包含:
INFO:werkzeug: * Running on http://0.0.0.0:5000/
For more info, visit http://docs.python.org/2/howto/logging.html
有关更多信息,请访问http://docs.python.org/2/howto/logging.html
Okay, as you insist that you cannot have two handler with the method I showed you, I'll add an example that makes this quite clear. First, add this logging code to your main:
好的,因为您坚持认为我向您展示的方法不能有两个处理程序,所以我将添加一个示例,使这一点非常清楚。首先,将此日志记录代码添加到您的主要:
import logging, logging.config, yaml
logging.config.dictConfig(yaml.load(open('logging.conf')))
Now also add some debug code, so that we see that our setup works:
现在还添加一些调试代码,以便我们看到我们的设置有效:
logfile = logging.getLogger('file')
logconsole = logging.getLogger('console')
logfile.debug("Debug FILE")
logconsole.debug("Debug CONSOLE")
All what is left is the "logging.conf" program. Let's use that:
剩下的就是“logging.conf”程序。让我们使用它:
version: 1
formatters:
hiformat:
format: 'HI %(asctime)s - %(name)s - %(levelname)s - %(message)s'
simple:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: hiformat
stream: ext://sys.stdout
file:
class: logging.FileHandler
level: DEBUG
formatter: simple
filename: errors.log
loggers:
console:
level: DEBUG
handlers: [console]
propagate: no
file:
level: DEBUG
handlers: [file]
propagate: no
root:
level: DEBUG
handlers: [console,file]
This config is more complicated than needed, but it also shows some features of the logging module.
这个配置比需要的更复杂,但它也显示了日志模块的一些特性。
Now, when we run our application, we see this output (werkzeug- and console-logger):
现在,当我们运行我们的应用程序时,我们会看到这个输出(werkzeug- 和 console-logger):
HI 2013-07-22 16:36:13,475 - console - DEBUG - Debug CONSOLE
HI 2013-07-22 16:36:13,477 - werkzeug - INFO - * Running on http://0.0.0.0:5000/
Also note that the custom formatter with the "HI" was used.
另请注意,使用了带有“HI”的自定义格式化程序。
Now look at the "errors.log" file. It contains:
现在查看“errors.log”文件。它包含了:
2013-07-22 16:36:13,475 - file - DEBUG - Debug FILE
2013-07-22 16:36:13,477 - werkzeug - INFO - * Running on http://0.0.0.0:5000/
回答by fleshgolem
Ok, my failure stemmed from two misconceptions:
好吧,我的失败源于两个误解:
1) Flask will apparently ignore all your custom logging unless it is running in production mode
1) Flask 显然会忽略您的所有自定义日志记录,除非它在生产模式下运行
2) debug=False is not enough to let it run in production mode. You have to wrap the app in any sort of WSGI server to do so
2) debug=False 不足以让它在生产模式下运行。您必须将应用程序包装在任何类型的 WSGI 服务器中才能这样做
After i started the app from gevent's WSGI server (and moving logging initialization to a more appropriate place) everything seems to work fine
在我从 gevent 的 WSGI 服务器启动应用程序(并将日志初始化移动到更合适的位置)之后,一切似乎都正常
回答by AsksAnyway
The output you see in the console of your app is from the underlying Werkzeug logger that can be accessed through logging.getLogger('werkzeug').
您在应用程序的控制台中看到的输出来自可以通过 logging.getLogger('werkzeug') 访问的底层 Werkzeug 记录器。
Your logging can function in both development and release by also adding handlers to that logger as well as the Flask one.
通过向该记录器和 Flask 记录器添加处理程序,您的日志记录可以在开发和发布中发挥作用。
More information and example code: Write Flask Requests to an Access Log.
更多信息和示例代码:将 Flask 请求写入访问日志。
回答by nicodjimenez
This works:
这有效:
if __name__ == '__main__':
import logging
logFormatStr = '[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s'
logging.basicConfig(format = logFormatStr, filename = "global.log", level=logging.DEBUG)
formatter = logging.Formatter(logFormatStr,'%m-%d %H:%M:%S')
fileHandler = logging.FileHandler("summary.log")
fileHandler.setLevel(logging.DEBUG)
fileHandler.setFormatter(formatter)
streamHandler = logging.StreamHandler()
streamHandler.setLevel(logging.DEBUG)
streamHandler.setFormatter(formatter)
app.logger.addHandler(fileHandler)
app.logger.addHandler(streamHandler)
app.logger.info("Logging is set up.")
app.run(host='0.0.0.0', port=8000, threaded=True)
回答by David
I didn't like the other answers so I kept at it and it seems like I had to make my logging config AFTER Flask did it's own setup.
我不喜欢其他答案,所以我一直坚持下去,似乎我必须在 Flask 完成自己的设置后进行日志记录配置。
@app.before_first_request
def initialize():
logger = logging.getLogger("your_package_name")
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter(
"""%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s"""
)
ch.setFormatter(formatter)
logger.addHandler(ch)
My app is structured like
我的应用程序的结构类似于
/package_name
__main__.py <- where I put my logging configuration
__init__.py <- conveniance for myself, not necessary
/tests
/package_name <- Actual flask app
__init__.py
/views
/static
/templates
/lib
Following these directions http://flask.pocoo.org/docs/0.10/patterns/packages/
回答by Michael Ekoka
Why not take a dive in the code and see...
为什么不深入研究代码,看看...
The module we land on is flask.logging.py
, which defines a function named create_logger(app)
. Inspecting that function will give a few clues as to potential culprits when troubleshooting logging issues with Flask.
我们登陆的模块是flask.logging.py
,它定义了一个名为 的函数create_logger(app)
。在对 Flask 的日志记录问题进行故障排除时,检查该函数将提供一些关于潜在罪魁祸首的线索。
EDIT: this answer was meant for Flask prior to version 1. The flask.logging.py
module has considerably changed since then. The answer still helps with some general caveats and advices regarding python logging, but be aware that some of Flask's peculiarities in that regard have been addressed in version 1 and might no longer apply.
编辑:此答案适用于版本 1 之前的 Flask flask.logging.py
。此后该模块发生了很大变化。答案仍然有助于解决有关 python 日志记录的一些一般警告和建议,但请注意,Flask 在这方面的一些特殊性已在版本 1 中得到解决,可能不再适用。
The first possible cause of conflicts in that function is this line:
该函数中第一个可能的冲突原因是这一行:
logger = getLogger(app.logger_name)
Let's see why:
让我们看看为什么:
The variable app.logger_name
is set in the Flask.__init__()
method to the value of import_name
, which is itself the receiving parameter of Flask(__name__)
. That is app.logger_name
is assigned the value of __name__
, which will likely be the name of your main package, let's for this example call it 'awesomeapp'.
该变量app.logger_name
在Flask.__init__()
方法中设置为 的值import_name
,它本身就是 的接收参数Flask(__name__)
。即app.logger_name
分配了 的值__name__
,这可能是您的主包的名称,让我们在此示例中将其称为“awesomeapp”。
Now, imagine that you decided to configure and create your own logger manually. What do you think the chances are that if your project is named "awesomeapp" you would also use that name to configure your logger, I think it's pretty likely.
现在,假设您决定手动配置和创建自己的记录器。你认为如果你的项目被命名为“awesomeapp”,你也会使用这个名字来配置你的记录器,我认为很有可能。
my_logger = logging.getLogger('awesomeapp') # doesn't seem like a bad idea
fh = logging.FileHandler('/tmp/my_own_log.log')
my_logger.setLevel(logging.DEBUG)
my_logger.addHandler(fh)
It makes sense to do this... except for a few problems.
这样做是有道理的……除了一些问题。
When the Flask.logger
property is invoked for the first time it will in turn call the function flask.logging.create_logger()
and the following actions will ensue:
当Flask.logger
第一次调用该属性时,它将依次调用该函数,flask.logging.create_logger()
并会发生以下操作:
logger = getLogger(app.logger_name)
Remember how you named your logger after the project and how app.logger_name
shares that name too? What happens in the line of code above is that the function logging.getLogger()
has now retrieved your previously created logger and the following instructions are about to mess with it in a way that will have you scratching your head later. For instance
还记得您如何在项目后命名记录器以及如何app.logger_name
共享该名称吗?在上面的代码行中发生的事情是,该函数logging.getLogger()
现在已经检索到了您之前创建的记录器,并且以下说明将以一种让您稍后挠头的方式对其进行处理。例如
del logger.handlers[:]
Poof, you just lost all the handlers you may have previously registered with your logger.
噗,您刚刚丢失了您之前可能在记录器中注册的所有处理程序。
Other things that happen within the function, without going much into details. It creates and registers two logging.StreamHandler
objects that can spit out to sys.stderr
and/or Response
objects. One for log level 'debug' and another for 'production'.
函数内发生的其他事情,无需过多赘述。它创建并注册两个logging.StreamHandler
可以吐出到sys.stderr
和/或Response
对象的对象。一个用于日志级别“调试”,另一个用于“生产”。
class DebugLogger(Logger):
def getEffectiveLevel(self):
if self.level == 0 and app.debug:
return DEBUG
return Logger.getEffectiveLevel(self)
class DebugHandler(StreamHandler):
def emit(self, record):
if app.debug and _should_log_for(app, 'debug'):
StreamHandler.emit(self, record)
class ProductionHandler(StreamHandler):
def emit(self, record):
if not app.debug and _should_log_for(app, 'production'):
StreamHandler.emit(self, record)
debug_handler = DebugHandler()
debug_handler.setLevel(DEBUG)
debug_handler.setFormatter(Formatter(DEBUG_LOG_FORMAT))
prod_handler = ProductionHandler(_proxy_stream)
prod_handler.setLevel(ERROR)
prod_handler.setFormatter(Formatter(PROD_LOG_FORMAT))
logger.__class__ = DebugLogger
logger.addHandler(debug_handler)
logger.addHandler(prod_handler)
With the above details to light it should become clearer why our manually configured logger and handlers misbehave when Flask gets involved. The new information gives us new options though. If you still want to keep separate handlers, the simplest approach is to name your logger to something different than the project (e.g. my_logger = getLogger('awesomeapp_logger')
). Another approach, if you want to be consistent with the logging protocols in Flask, is to register a logging.FileHandler
object on Flask.logger
using a similar approach to Flask.
有了上面的细节,就应该更清楚为什么我们手动配置的记录器和处理程序在 Flask 参与时会出现错误行为。不过,新信息为我们提供了新的选择。如果您仍想保留单独的处理程序,最简单的方法是将记录器命名为与项目不同的名称(例如my_logger = getLogger('awesomeapp_logger')
)。另一种方法,如果您想与 Flask 中的日志记录协议保持一致,则使用与 Flask 类似的方法注册一个logging.FileHandler
对象Flask.logger
。
import logging
def set_file_logging_handler(app):
logging_path = app.config['LOGGING_PATH']
class DebugFileHandler(logging.FileHandler):
def emit(self, record):
# if your app is configured for debugging
# and the logger has been set to DEBUG level (the lowest)
# push the message to the file
if app.debug and app.logger.level==logging.DEBUG:
super(DebugFileHandler, self).emit(record)
debug_file_handler = DebugFileHandler('/tmp/my_own_log.log')
app.logger.addHandler(debug_file_handler)
app = Flask(__name__)
# the config presumably has the debug settings for your app
app.config.from_object(config)
set_file_logging_handler(app)
app.logger.info('show me something')