为什么不深入代码看一看呢...
我们要查看的模块是`flask.logging.py`,它定义了一个名为`create_logger(app)`的函数。检查该函数将提供一些线索,有助于排除使用Flask进行日志记录时出现的问题。
编辑:此答案适用于版本1之前的Flask。此后,`flask.logging.py`模块已经发生了很大变化。答案仍然有助于在python日志记录方面提供一些普遍的注意事项和建议,但请注意,Flask在这方面的某些特殊性已在版本1中得到解决,可能不再适用。
该函数中冲突的第一个可能原因是以下行:
logger = getLogger(app.logger_name)
让我们来看看原因:
变量
app.logger_name
是在
Flask.__init__()
方法中设置的,其值为
import_name
。而
import_name
本身是
Flask(__name__)
的接收参数。也就是说,
app.logger_name
被赋值为
__name__
的值,
__name__
很可能是您的主包的名称,例如在这个例子中叫做'awesomeapp'。
现在,假设您决定手动配置并创建自己的记录器。如果您的项目名称为“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)
这样做是有道理的...但还存在一些问题。
当首次调用
Flask.logger
属性时,它将依次调用函数
flask.logging.create_logger()
,并会随之发生以下操作:
logger = getLogger(app.logger_name)
记得你之前是如何以项目名称来命名你的日志记录器,而
app.logger_name
也与这个名称相同吗?在上面这行代码中,
logging.getLogger()
函数已经检索到了你之前创建的日志记录器,并且接下来的指令将会以一种让你后悔不已的方式对其进行修改。例如:
del logger.handlers[:]
瞬间,您可能先前已经注册的日志处理程序全部消失了。
函数内还有其他事情发生,不再详细说明。它创建和注册两个 logging.StreamHandler
对象,可以输出到 sys.stderr
和/或 Response
对象。一个用于记录级别为“debug”,另一个用于“production”。
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)
有了上述细节,我们可以更清楚地了解当 Flask 参与时为什么手动配置的日志记录器和处理程序会出现问题。不过,新的信息为我们提供了新的选择。如果您仍想保持单独的处理程序,最简单的方法是将记录器命名为与项目不同的名称(例如,
my_logger = getLogger('awesomeapp_logger')
)。另一种方法,如果您想与 Flask 中的日志记录协议保持一致,是使用与 Flask 类似的方法在
Flask.logger
上注册一个
logging.FileHandler
对象。
import logging
def set_file_logging_handler(app):
logging_path = app.config['LOGGING_PATH']
class DebugFileHandler(logging.FileHandler):
def emit(self, record):
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__)
app.config.from_object(config)
set_file_logging_handler(app)
app.logger.info('show me something')
app.logger
的问题在于它完全是硬编码的。至少在Flask 0.12.2
中,Flask 的日志记录设置(日志级别、日志格式和日志输出流)都是硬编码在 flask/logging.py 中的,无法在不覆盖app.logger
属性并编写自定义实现的情况下更改。 - alexykot