1 日志保存与输出
1.0 测试用例
import logging
from logging import handlers
from flask import Flask
app = Flask(__name__)
class Logger(object):
level_relations = {
'debug':logging.DEBUG,
'info':logging.INFO,
'warning':logging.WARNING,
'error':logging.ERROR,
'crit':logging.CRITICAL
}#日志级别关系映射
def __init__(self,filename,level='info',when='D',backCount=3,fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
'''调用Logger类设定日志文件名称,以操作日志方法'''
self.logger = logging.getLogger(filename)
'''设置日志格式'''
format_str = logging.Formatter(fmt)
'''设置日志级别'''
self.logger.setLevel(self.level_relations.get(level))
'''屏幕输出日志信息'''
sh = logging.StreamHandler()
'''设置屏幕日志信息格式'''
sh.setFormatter(format_str)
'''指定间隔时间自动生成日志文件'''
th = handlers.TimedRotatingFileHandler(filename=filename,when=when,backupCount=backCount,encoding='utf-8')
'''设定写入文件的日志格式'''
th.setFormatter(format_str)
'''日志信息添加到Logger类变量hanlers列表中'''
self.logger.addHandler(sh)
self.logger.addHandler(th)
@app.route("/save_log", methods=["POST"])
def save_log():
log = Logger('all.log',level='debug')
log.logger.debug('debug')
log.logger.info('info')
log.logger.warning('警告')
log.logger.error('报错')
log.logger.critical('严重')
return "success"
if __name__ == '__main__':
app.run(port=8090, debug=True)
1.2 日志文件
all.log
2019-04-01 15:42:54,519 - log_save.py[line:37] - DEBUG: debug
2019-04-01 15:42:54,519 - log_save.py[line:38] - INFO: info
2019-04-01 15:42:54,520 - log_save.py[line:39] - WARNING: 警告
2019-04-01 15:42:54,520 - log_save.py[line:40] - ERROR: 报错
2019-04-01 15:42:54,520 - log_save.py[line:41] - CRITICAL: 严重
2 logging模块解析
2.1 初始化文件
__init__
getLogger函数:
def getLogger(name=None):
"""
返回指定名称的logger,若未指定名称返回root.
"""
if name:
return Logger.manager.getLogger(name)
else:
return root
Logger.manager = Manager(Logger, root)
class Manager(object):
"""
日志管理实例.
"""
def __init__(self, rootnode):
"""
初始化.
"""
self.root = rootnode
self.disable = 0
self.emittedNoHandlerWarning = False
self.loggerDict = {}
self.loggerClass = None
self.logRecordFactory = None
def getLogger(self, name):
"""
获取日志.
"""
rv = None
if not isinstance(name, str):
raise TypeError('A logger name must be a string')
_acquireLock()
try:
if name in self.loggerDict:
rv = self.loggerDict[name]
if isinstance(rv, PlaceHolder):
ph = rv
rv = (self.loggerClass or _loggerClass)(name)
rv.manager = self
self.loggerDict[name] = rv
self._fixupChildren(ph, rv)
self._fixupParents(rv)
else:
rv = (self.loggerClass or _loggerClass)(name)
rv.manager = self
self.loggerDict[name] = rv
self._fixupParents(rv)
finally:
_releaseLock()
return rv
_loggerClass = Logger
class Logger(Filterer):
"""
日志级别控制.
"""
def __init__(self, name, level=NOTSET):
"""
初始化文件名和日志级别.
"""
Filterer.__init__(self)
self.name = name
self.level = _checkLevel(level)
self.parent = None
self.propagate = True
self.handlers = []
self.disabled = False
self._cache = {}
def setLevel(self, level):
"""
日志级别.
"""
self.level = _checkLevel(level)
self.manager._clear_cache()
def debug(self, msg, *args, **kwargs):
"""
debug级别.
"""
if self.isEnabledFor(DEBUG):
self._log(DEBUG, msg, args, **kwargs)
def info(self, msg, *args, **kwargs):
"""
info级别
"""
if self.isEnabledFor(INFO):
self._log(INFO, msg, args, **kwargs)
def warning(self, msg, *args, **kwargs):
"""
warning级别.
"""
if self.isEnabledFor(WARNING):
self._log(WARNING, msg, args, **kwargs)
def warn(self, msg, *args, **kwargs):
warnings.warn("The 'warn' method is deprecated, "
"use 'warning' instead", DeprecationWarning, 2)
self.warning(msg, *args, **kwargs)
def error(self, msg, *args, **kwargs):
"""
error级别.
"""
if self.isEnabledFor(ERROR):
self._log(ERROR, msg, args, **kwargs)
def exception(self, msg, *args, exc_info=True, **kwargs):
"""
用异常信息记录错误的方法.
"""
self.error(msg, *args, exc_info=exc_info, **kwargs)
def critical(self, msg, *args, **kwargs):
"""
critical级别.
"""
if self.isEnabledFor(CRITICAL):
self._log(CRITICAL, msg, args, **kwargs)
def addHandler(self, hdlr):
_acquireLock()
try:
if not (hdlr in self.handlers):
self.handlers.append(hdlr)
finally:
_releaseLock()
2.2 句柄文件
handlers.py
class TimedRotatingFileHandler(BaseRotatingHandler):
"""
日志保存句柄:时间间隔操作日志.
若 backupCount(备份文件数量) > 0, 回滚结束后,不再保存之前的文件,并删除最旧的文件.
"""
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
self.when = when.upper()
self.backupCount = backupCount
self.utc = utc
self.atTime = atTime
# 计算回滚时间.
# S - 秒
# M - 分
# H - 时
# D - 天
# midnight - 午夜回滚
# W{0-6} - 指定在星期几回滚; 0 - 星期一
if self.when == 'S':
self.interval = 1 # one second
self.suffix = "%Y-%m-%d_%H-%M-%S"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
elif self.when == 'M':
self.interval = 60 # one minute
self.suffix = "%Y-%m-%d_%H-%M"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
elif self.when == 'H':
self.interval = 60 * 60 # one hour
self.suffix = "%Y-%m-%d_%H"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
elif self.when == 'D' or self.when == 'MIDNIGHT':
self.interval = 60 * 60 * 24 # one day
self.suffix = "%Y-%m-%d"
self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
elif self.when.startswith('W'):
self.interval = 60 * 60 * 24 * 7 # one week
if len(self.when) != 2:
raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
if self.when[1] < '0' or self.when[1] > '6':
raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
self.dayOfWeek = int(self.when[1])
self.suffix = "%Y-%m-%d"
self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
else:
raise ValueError("Invalid rollover interval specified: %s" % self.when)
log_save.py
3 总结
日志记录流程:
[参考文献]
[1]https://github.com/python/cpython/blob/3.7/Lib/logging/init.py
[2]https://github.com/python/cpython/blob/3.7/Lib/logging/handlers.py