python项目 多进程日志分割

python 日志分割 (踩坑实录)

前文说到uwsgi日志分割,那个是属于uwsgi项目的运行日志,我们如果需要打印一些信息的话,如果全放在里面由于太多且多线程多进程会导致日志错乱,所以我决定再增加一个程序运行日志模块,然后踩坑开始了。

一、采用TimedRotatingFileHandler,分割when=‘D’

我一开始百度按日期分割日志,全是这个的消息,然后在本地测试了一下小时和分钟的,没什么问题,就改了改放服务器上跑了,代码如下:

def setup_log(log_name):
    # 创建logger对象。传入logger名字
    logger = logging.getLogger(log_name)
    log_path = os.path.join("../project_log", log_name)
    # 设置日志记录等级
    logger.setLevel(logging.DEBUG)
    # interval 滚动周期,
    # when="D", interval=1 表示每天生成一个文件
    # backupCount  表示日志保存个数
    file_handler = TimedRotatingFileHandler(
        filename=log_path, when="D", interval=1, backupCount=30
    )
    # filename="mylog" suffix设置,会生成文件名为mylog.2020-02-25.log
    # file_handler.suffix = "%Y-%m-%d.log"
    # extMatch是编译好正则表达式,用于匹配日志文件名后缀
    # 需要注意的是suffix和extMatch一定要匹配的上,如果不匹配,过期日志不会被删除。
    # file_handler.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}.log$")
    # 定义日志输出格式
    file_handler.setFormatter(
        logging.Formatter(
            "[%(asctime)s] [%(thread)d] [%(process)d] [%(levelname)s] - %(module)s.%(funcName)s (%(filename)s:%(lineno)d) - %(message)s"
        )
    )
    logger.addHandler(file_handler)
    return logger

然后运行了两天都发现日志没有分割成功,然后各种调整也没用,最后发现’D’选项是每次程序启动,TimedRotatingFileHandler就会在内部记录一个一天后的时间,时间到了才去分割日志,由于我们项目还在开发测试中,每天都重启几次,所以导致一直不会分割,因此要更改其他方案。

二、采用TimedRotatingFileHandler,分割when=‘MIDNIGHT’

我一开始看TimedRotatingFileHandler类的初始化代码,以为’D’选项和’MIDNIGHT’选项是相同的,其实不然,'D’是代表要过一整天才分割而’MIDNIGHT’是表示午夜0点一到就分割,显然更适用于我们的项目现况,于是兴高采烈的修改上线,第二天到了一看,裂开,分割是分割了,怎么分割出的文件内容反而今天的,昨天的日志全不见了!!!!后来一顿百度,发现是多进程导致的。我们在uwsgi.ini中设置了4个process,这样的话意味着在程序中有四个进程会在0点触发对这个文件的搬运操作,那么问题就这样产生了,如下:
看了一下 TimedRotatingFileHandler 的 doRollover 方法:

t = self.rolloverAt - self.interval
if self.utc:
    timeTuple = time.gmtime(t)
else:
    timeTuple = time.localtime(t)
dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
if os.path.exists(dfn):
    os.remove(dfn)
os.rename(self.baseFilename, dfn)

每个进程在过了 rotate 时间点之后写第一条日志的时候,都会执行这个 doRollover,然后看目标文件是否存在,存在就删除,然后再把原日志文件重命名,这样四个进程一顿操作,最前面的当然就被删除不见了,因此这个巨坑,又得换方案!

三、WatchedFileHandler + cron 定时方案

后来重新找logging库的其他FileHandler,发现了WatchedFileHandler,这这个 FileHandler 用于监视它所写入日志记录的文件。 如果文件发生变化,它会被关闭并使用文件名重新打开。(并且如果文件不存在,会创建之)
那这样的话,我们只需要改用这个,然后在写一个定时脚本来执行mv操作即可,刚刚好之前uwsgi也有一个定时分割日志的脚本,直接加在里面,搞掂!

def setup_log(log_name):
    # 创建logger对象。传入logger名字
    logger = logging.getLogger(log_name)
    log_path = os.path.join("../project_log", log_name)
    # 设置日志记录等级
    logger.setLevel(logging.DEBUG)
    file_handler = WatchedFileHandler(filename=log_path)
    file_handler.setFormatter(
        logging.Formatter(
            "[%(asctime)s] [%(thread)d] [%(process)d] [%(levelname)s] - %(module)s.%(funcName)s (%(filename)s:%(lineno)d) - %(message)s"
        )
    )
    logger.addHandler(file_handler)
    return logger

注意: [%(thread)d] [%(process)d]这两个选项对于多线程多进程定位问题时有很大的帮助,虽然日志仍然会错乱,但是定位问题时更加精准。如果想要日志不错乱,应该需要加进程锁,很麻烦而且严重影响效率,鄙人暂时才疏学浅,等以后有需要再继续学习!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值