因为工作需要,要求写一个能够按日期路径保存,按时间滚动切分的日志方法。所以重写了logging模块中TimedRotatingFileHandler的doRollover方法(实际上只是改了改前人的代码)。
完整代码如下:logUtil.py。
import logging
import os
import datetime
import time
from logging.handlers import TimedRotatingFileHandler
gl_baseDir = ''
class TimeLoggerRolloverHandler(TimedRotatingFileHandler):
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False,
atTime=None):
super(TimeLoggerRolloverHandler, self).__init__(filename, when, interval, backupCount, encoding, delay, utc, atTime)
def doRollover(self):
"""
TimedRotatingFileHandler对日志的切分是在满足设定的时间间隔后,执行doRollover方法,
将my.log重命名为带有当前时间后缀(my.log.****)的文件,并新建一个my.log,继续记录后续日志。
(1) 重写TimedRotatingFileHandler的doRollover方法的文件翻转块代码
做了以下两点改动:
重定义了新文件名,将日期放在了中间而不是最后
直接将将baseFilename 指向新文件
"""
if self.stream:
self.stream.close()
self.stream = None
currentTime = int(time.time())
dstNow = time.localtime(currentTime)[-1]
# 重新定义了新文件名
global gl_baseDir
_, base_name = os.path.split(self.baseFilename)
base_name = base_name.split('_')[0] # 这里要求文件名前缀不能有下滑线
print(f'baseFilename: {self.baseFilename}')
print(f'base_name:{base_name}')
datetime_now = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S').split('_')
date_now = datetime_now[0]
time_now = datetime_now[1]
log_dir = f'{gl_baseDir}/{date_now}_{time_now}'
if not os.path.exists(log_dir):
os.makedirs(log_dir, exist_ok=True)
dfn = f"{log_dir}/{base_name}_{date_now}_{time_now}.log"
if os.path.exists(dfn):
os.remove(dfn)
self.baseFilename = dfn
if not self.delay:
self.stream = self._open()
newRolloverAt = self.computeRollover(currentTime)
while newRolloverAt <= currentTime:
newRolloverAt = newRolloverAt + self.interval
# If DST changes and midnight or weekly rollover, adjust for this.
if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
dstAtRollover = time.localtime(newRolloverAt)[-1]
if dstNow != dstAtRollover:
if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
addend = -3600
else: # DST bows out before next rollover, so we need to add an hour
addend = 3600
newRolloverAt += addend
self.rolloverAt = newRolloverAt
def get_logger(project_name, log_file_name, when='H', interval=1):
global gl_baseDir
gl_baseDir = f"./log_{project_name}"
datetime_now = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S').split('_')
date_now = datetime_now[0]
time_now = datetime_now[1]
log_dir = f'{gl_baseDir}/{date_now}'
if not os.path.exists(log_dir):
os.makedirs(log_dir, exist_ok=True)
log_file = f"{log_dir}/{log_file_name}_{date_now}_{time_now}.log"
logger = logging.getLogger('log') # 创建日志收集器
logger.setLevel(logging.DEBUG) # 定义收集级别,低于该级别将被过滤,DEBUG是最低级别
console_handler = logging.StreamHandler() # 输出到控制台
save_handler = TimeLoggerRolloverHandler(log_file, when=when, interval=interval) # 输出到文件
console_handler.setLevel(logging.DEBUG) # 定义控制台输出级别
save_handler.setLevel(logging.DEBUG) # 定义文件输出级别
formatter = logging.Formatter('%(asctime)s-%(levelname)s-%(filename)s-%(module)s-'
'%(funcName)s-%(lineno)s-%(message)s') # 指定输出格式
console_handler.setFormatter(formatter) # 规定日志输出的时候按照formatter格式来打印
save_handler.setFormatter(formatter)
logger.addHandler(console_handler) # 对接,添加输出控制台渠道
logger.addHandler(save_handler) # 对接,添加输出文件渠道
return logger
if __name__ == "__main__":
my_logger = get_logger("project_name", "log_filename", when='S', interval=1)
for i in range(10):
my_logger.error('111')
time.sleep(1)
如果想要日志方法跨文件全局调用的话,可以创建一个公用的py文件,事先初始化log方法,以后直接从这个文件调用相关方法。
commonObj.py
import logUtil
import os
gl_log = None
def init_log():
global gl_log
log_path = 'L0send_log'
gl_log = logUtil.get_logger(log_path, 'L0send', when='s', interval=1)
return gl_log
def get_log():
global gl_log
return gl_log
main.py
from commonObj import init_log
import time
log = init_log()
for i in range(5):
log.debug('1')
log.info('1')
log.warning('1')
log.error('1')
time.sleep(1)
t.py
from commonObj import get_log
import time
def test():
log = get_log()
test1 = 'test log'
print('test log')
for i in range(5):
log.debug(test1)
log.info(test1)
log.warning(test1)
log.error(test1)
time.sleep(1)