Flask日志-werkzeug和flask应用的日志为什么会输出到一个日志文件
日志的配置
logger.py
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# log_path =''
log_name = 'loger.log'
fh = logging.FileHandler(log_name, mode='a')
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
logger.addHandler(fh)
app.py
from flask import Flask
from logger import logger
app = Flask(__name__)
@app.route('/route')
def hello():
logger.info('123')
app.logger.warning('456')
return 'hello world!'
访问/route日志是这样的
2021-04-26 14:59:51,629 - _internal.py[line:113] - WARNING: * Debugger is active!
2021-04-26 14:59:51,641 - _internal.py[line:113] - INFO: * Debugger PIN: 332-792-581
2021-04-26 14:59:51,684 - _internal.py[line:113] - INFO: * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
2021-04-26 15:03:52,962 - __init__.py[line:32] - INFO: 123
2021-04-26 15:03:52,962 - __init__.py[line:33] - WARNING: 456
2021-04-26 15:03:52,964 - _internal.py[line:113] - INFO: 127.0.0.1 - - [26/Apr/2021 15:03:52] "[37mGET /route HTTP/1.1[0m" 200 -
werkzeug日志
2021-04-26 15:03:52,964 - _internal.py[line:113] - INFO: 127.0.0.1 - - [26/Apr/2021 15:03:52] "[37mGET /route HTTP/1.1[0m" 200 -
这条日志是werkzeug产生的,在_internal.py可以看到
def _log(type, message, *args, **kwargs):
"""Log into the internal werkzeug logger."""
global _logger
if _logger is None:
import logging
_logger = logging.getLogger('werkzeug')
# Only set up a default log handler if the
# end-user application didn't set anything up.
if not logging.root.handlers and _logger.level == logging.NOTSET:
_logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
_logger.addHandler(handler)
getattr(_logger, type)(message.rstrip(), *args, **kwargs)
可以看到,该logger使用的name是werkzeug
当logging.root.handlers
即logging.RootLogger
对象不存在时,会为logger添加StreamHandler,从而我们在控制台可以看到该信息
当存在logging.RootLogger
对象时,会根据RootLogger
定义的格式,输出到文件或控制台
app日志
falsk创建日志对象
def logger(self):
return create_logger(self)
def create_logger(app):
logger = logging.getLogger(app.name)
# 1.1.0 changes name of logger, warn if config is detected for old
# name and not new name
for old_name in ("flask.app", "flask"):
old_logger = logging.getLogger(old_name)
if _has_config(old_logger) and not _has_config(logger):
warnings.warn(
"'app.logger' is named '{name}' for this application,"
" but configuration was found for '{old_name}', which"
" no longer has an effect. The logging configuration"
" should be moved to '{name}'.".format(name=app.name, old_name=old_name)
)
break
if app.debug and not logger.level:
logger.setLevel(logging.DEBUG)
if not has_level_handler(logger):
logger.addHandler(default_handler)
return logger
可以看到,该logger使用的name是app.name
为什么两个不同的logger会输出到同一个日志文件?
我们看`logging.getLogger()的代码
def getLogger(name=None):
"""
Return a logger with the specified name, creating it if necessary.
If no name is specified, return the root logger.
"""
if not name or isinstance(name, str) and name == root.name:
return root
return Logger.manager.getLogger(name)
class RootLogger(Logger):
"""
A root logger is not that different to any other logger, except that
it must have a logging level and there is only one instance of it in
the hierarchy.
"""
def __init__(self, level):
"""
Initialize the logger with the name "root".
"""
Logger.__init__(self, "root", level)
可以看到,当getlogger()
不传入参数时,会返回一个
Logger是层次结构的,使用 '.' 点号分割
import logging
root = logging.getLogger()
logger = logging.getLogger(__name__)
logger1 = logging.getLogger(__name__ + ".child")
print(root.name,type(root),root.parent,id(root))
root None 2265089600672
print(logger.name, type(logger), id(logger), id((logger.parent)))
__main__ 2265139887408 2265089600672
print(logger1.name, type(logger1), id(logger1), id((logger1.parent)))
__main__.child 2265139884384 2265139887408
子对象会将记录共享到父对象,所以RootLogger
会包含所有子对象的记录并将其记录到文件