目标是想让代码中的日志按照不同的级别保存到不同的文件,即不管是框架本身的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都按级别全部保存在对应的文件中了。