Django 多进程下日志切割导致错乱问题解决方案

问题产生的原因

  1. 在单进程下日志的处理逻辑:
# 设定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文件
  1. 在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 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()
posted @ 2020-12-05 10:50  ugvibib  阅读(310)  评论(0编辑  收藏  举报