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] "GET /route HTTP/1.1" 200 -

werkzeug日志

2021-04-26 15:03:52,964 - _internal.py[line:113] - INFO: 127.0.0.1 - - [26/Apr/2021 15:03:52] "GET /route HTTP/1.1" 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.handlerslogging.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会包含所有子对象的记录并将其记录到文件