Flask 中日志按照日志级别分开保存到不同文件

目标是想让代码中的日志按照不同的级别保存到不同的文件,即不管是框架本身的debug日志还是我们自己写的debug日志都保存到debug.log,info日志都保存在info.log,以此类推。

如果是为不同级别设置不同的logger,每个logger对应不同的文件handler,然后封装自己的日志函数调用对应的logger来记录日志确实是可以记录到不同的文件,但是这样只能记录自己代码中打的log而不会记录flask框架打印的log,因为框架使用的logger名称和我们使用的logger不一样,要达到我们的目标不能在logger上做处理,应该对和框架同一个logger上的handler做处理。

这里主要说的方式是使用logging的setLoggerClass方法为logging设置自己的logger对handler的处理逻辑。

默认的在python的logging库中会使用默认的Logger,在默认的Logger中有一个 callHandlers 的方法,里面会遍历该logger上添加的所有handler,只要调用的日志record的级别大于或等于设置的handler的级别就会执行handler中的handle函数。那么如果重写logger这里的逻辑,只当record的级别等于handler设置的级别时才会触发实际的handle函数这样就可以通过设置handler的级别就能让这个handler只记录对应级别的日志。

因此可以继承默认的Logger后重写callHandlers逻辑实现新的自定义Logger,通过setLoggerClass方法为logging模块设置新的Logger,然后为所有想要的级别加上文件handler,handler对应不同的日志文件,这样logger在记录对应级别的日志的时候就自动记录到对应的文件了。

由于我们需要同时保存自己的log和flask框架的log,所以必须将logger的名字设置为flask的logger名称:werkzeug,才能同时保存两者的日志到同一文件中即大家都使用werkzeug这个logger。

file: log.py:

import os
import logging
import logging.handlers
import sys
from logging import raiseExceptions
from logging import Logger

LOG_PATH = '/tmp/'


class AppLogger(Logger):
    '''自定义logger
    如果handler名称为console表示在终端打印所有大于等于设置级别的日志
    其他handler则只记录等于设置级别的日志'''

    def __init__(self, name, level=logging.NOTSET):
        super(AppLogger, self).__init__(name, level)

    def callHandlers(self, record):
        """
        Pass a record to all relevant handlers.

        Loop through all handlers for this logger and its parents in the
        logger hierarchy. If no handler was found, output a one-off error
        message to sys.stderr. Stop searching up the hierarchy whenever a
        logger with the "propagate" attribute set to zero is found - that
        will be the last logger whose handlers are called.
        """
        c = self
        found = 0
        while c:
            for hdlr in c.handlers:
                found = found + 1
                if hdlr.name == 'console':
                    if record.levelno >= hdlr.level:
                        hdlr.handle(record)
                else:
                    if record.levelno == hdlr.level:
                        hdlr.handle(record)
            if not c.propagate:
                c = None  # break out
            else:
                c = c.parent
        if (found == 0) and raiseExceptions and not self.manager.emittedNoHandlerWarning:  # noqa
            sys.stderr.write("No handlers could be found for logger"
                            " \"%s\"\n" % self.name)
            self.manager.emittedNoHandlerWarning = 1


def get_logger(logfile_name=__name__, log_path=LOG_PATH):
    '''save log to diffrent file by deffirent log level into the log path
    and print all log in console'''
    logging.setLoggerClass(AppLogger)
    formatter = logging.Formatter(
        '%(asctime)s %(name)s %(levelname)s %(message)s', '%Y-%m-%d %H:%M:%S')

    log_files = {
        logging.DEBUG: os.path.join(log_path, logfile_name + '-debug.log'),
        logging.INFO: os.path.join(log_path, logfile_name + '-info.log'),
        logging.WARNING: os.path.join(log_path, logfile_name + '-warning.log'),
        logging.ERROR: os.path.join(log_path, logfile_name + '-error.log'),
        logging.CRITICAL:
        os.path.join(log_path, logfile_name + '-critical.log')
    }
    # 和flask默认使用同一个logger
    logger = logging.getLogger('werkzeug')
    logger.setLevel(logging.DEBUG)
    for log_level, log_file in log_files.items():
        file_handler = logging.handlers.TimedRotatingFileHandler(log_file,
                                                                'midnight')
        file_handler.setLevel(log_level)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

    console_handler = logging.StreamHandler()
    console_handler.name = "console"
    console_handler.setLevel(logging.DEBUG)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    return logger


logger = get_logger()

通过以上代码为logging设置自定义Logger并添加对应的handler后即可在终端打印所有日志,在文件中则是将日志内容按日志级别分开保存。

file: server.py

from log import logger
from flask import Flask
app = Flask(__name__)
app.debug = True


@app.route("/")
def hello():
    return "Hello World!"

logger.debug('----')
logger.info('----')
logger.error('----')
logger.warning('----')
app.run()

运行以上的flask示例,可以在终端看到全部日志:

2017-11-12 14:54:36 werkzeug DEBUG ----
2017-11-12 14:54:36 werkzeug INFO ----
2017-11-12 14:54:36 werkzeug ERROR ----
2017-11-12 14:54:36 werkzeug WARNING ----
2017-11-12 14:54:36 werkzeug INFO  * Restarting with stat
2017-11-12 14:54:36 werkzeug DEBUG ----
2017-11-12 14:54:36 werkzeug INFO ----
2017-11-12 14:54:36 werkzeug ERROR ----
2017-11-12 14:54:36 werkzeug WARNING ----
2017-11-12 14:54:36 werkzeug WARNING  * Debugger is active!
2017-11-12 14:54:36 werkzeug INFO  * Debugger PIN: 971-444-041
2017-11-12 14:54:36 werkzeug INFO  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

同时在tmp目录下生产了对应级别的日志文件,终端中出现的所有log都按级别全部保存在对应的文件中了。


也可以看看


全国大流量卡免费领

19元月租ㆍ超值优惠ㆍ长期套餐ㆍ免费包邮ㆍ官方正品