Django 多进程下日志切割导致错乱问题解决方案
问题产生的原因
- 在单进程下日志的处理逻辑:
# 设定1小时的切割间割
第1步: 生成info.log
第2步: 写入1小时日志
第3步: 1小时之后,判定info.log-2018-11-30-1文件是否存在,如果存在则删除,然么重命名info.log为info.log-2018-11-30-1
第4步: 重新生成info.log并将logger的文件句柄指向新的info.log文件
- 在2个进程下日志的处理逻辑:
# 设定1小时的切割间割
第1步: 生成info.log
第2步: 写入1小时日志
第3步: 1小时之后,1号进程先判定info.log-2018-11-30-1文件是否存在,如果存在则删除,然么重命名info.log为info.log-2018-11-30-1
第4步: 此时可能2号进程还在向info.log文件进行写入,由于写入流已经打开,文件句柄指向的是原info.log, 即现在的info.log-2018-11-30-1,所以2进程会向info.log-2018-11-30-1中写入
第5步: 等到2号进程下次写日志时,发现到了执行重命名操作的时间了,但此时info.log-2018-11-30-1已经存在,然后他会进行删除操作,这样就会导致info.log-2018-11-30-1已存在的日志丢失
第6步: 2号进程执行删除操作后,接下来便会执行将info.log重命名为info.log-2018-11-30-1的操作,这时候就会导致1号进行继续向info.log-2018-11-30-1写入,这样就导致了日志错乱
解决方案
python 日志文件写入的时候,加入文件独占锁
基于此方案,目前有两个安装包实现这个功能, 安装任何一个即可:
- concurrent-log-handler, 安装方式
pip install concurrent-log-handler
- pyloghandlers, 安装方式
pip install pyloghandlers
concurrent-log-handler
github上面一位大佬实现的,目前版本v0.9.20仅支持按文件大小切割文件
pyloghandlers
是我基于 concurrent-log-handler
在保留核心功能的基本上做了一些改动,使其支持按大小、按日期切割文件;
下面是pyloghandlers
一个简短的用法介绍, 更详细的用法,可以查看文档 pyloghandlers On Github:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
},
},
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'pyloghandlers.handles.PylogRotatingFileHandler',
'formatter': 'default',
'filename': 'info.log',
'max_bytes': 1024,
'backup_count': 5,
},
'time': {
'level': 'DEBUG',
'class': 'pyloghandlers.handles.PylogTimedRotatingFileHandler',
'formatter': 'default',
'filename': 'info.log',
'when': 'S',
'interval': 1,
'backup_count': 100,
'encoding': 'utf-8'
}
},
'loggers': {
'my_app': {
# 'handlers': ['time'],
'handlers': ['file'],
'level': 'DEBUG',
},
}
}
def write_log(tag):
import logging.config
logging.config.dictConfig(LOGGING)
logger = logging.getLogger('my_app')
for i in range(0, 500):
# import time
# time.sleep(0.001)
logger.debug(f'[{str(tag)*2}] process tag {i}')
if __name__ == '__main__':
from multiprocessing import Process
p_count = 1
p_list = []
for i in range(p_count):
p_list.append(Process(target=write_log, args=(i+1,)))
for p in p_list:
p.start()
for p in p_list:
p.join()