目标是想让代码中的日志按照不同的级别保存到不同的文件,即不管是框架本身的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。
::: python 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后即可在终端打印所有日志,在文件中则是将日志内容按日志级别分开保存。
::: python 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示例,可以在终端看到全部日志:
::: text 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都按级别全部保存在对应的文件中了。
网友54.*.*.91[法国]2022-06-30 05:32
网友216.*.*.226[Seattle]2022-06-30 05:25
网友60.*.*.104[火星]2022-06-30 05:13
网友157.*.*.144[Redmond]2022-06-30 05:00
你好,在使用的时候发现一个问题:系统在运行的时候,会以app的名字创建一个logger,加上flask名字'werkzeug'对应的logger,一共至少是两个logger。比如您这的app是server.py,会创建server名字命名的logger和werkzeug命名的logger。当您在server.py中出现特定错误,如调用某个不存在的函数时,出现的错误会打印在server命名的logger里,但是由于callHandlers()函数里没有调用该logger的条件,所以对应的日志不会打印。2019-10-16 15:05 回复
你好,在使用的时候发现一个问题:系统在运行的时候,会以app的名字创建一个logger,加上flask名字'werkzeug'对应的logger,一共至少是两个logger。比如您这的app是server.py,会创建server名字命名的logger和werkzeug命名的logger。当您在server.py中出现特定错误,如调用某个不存在的函数时,出现的错误会打印在server命名的logger里,但是由于callHandlers()函数里没有调用该logger的条件,所以对应的日志不会打印。2019-10-16 15:05 回复
发表评论
亲~ 评论内容是必须的哟! o(∩_∩)o
昵称
邮箱
主页
评论