Python 多进程日志输出到同一个文件并实现日志回滚

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Python 多进程想要实现将日志输出到同一个文件中,使用同一个日志句柄,且日志需要按照日期,大小回滚。


Python 多进程想要实现将日志输出到同一个文件中,使用同一个日志句柄,且日志需要按照日期,大小回滚。

出现问题

使用 Python 自带的有回滚功能的日志库 TimedRotatingFileHandler 日志并没有正常打印:在日志回滚时多个进程抢占句柄,最终只有一个进程能够正常打印日志;在多进程中回滚时会互相将日志文件删除,并且多进程同时启动时有小概率将多进程卡死。

注:使用该库的日志滚动只会发生在有新日志输出时,会出现像这样的情况:
假设日志按天回滚,第一天日志正常输出,第二天和第三天没有日志输出,第四天有日志输出,那么在第四天只会回滚第一天的日志,而不会生成第二天和第三天的日志文件。

TimedRotatingFileHandler 回滚 doRollover() 源码(省略初始化和其他函数部分):

class TimedRotatingFileHandler(BaseRotatingHandler):
    def doRollover(self):
        """
        do a rollover; in this case, a date/time stamp is appended to the filename
        when the rollover happens.  However, you want the file to be named for the
        start of the interval, not the current time.  If there is a backup count,
        then we have to get a list of matching filenames, sort them and remove
        the one with the oldest suffix.
        """
        # 关闭向日志中打印的被回滚掉的流,将其流置为空
        if self.stream:
            self.stream.close()
            self.stream = None

        # 获得队列的起始时间,将值赋给TimeTuple
        currentTime = int(time.time())
        dstNow = time.localtime(currentTime)[-1]
        t = self.rolloverAt - self.interval
        if self.utc:
            timeTuple = time.gmtime(t)
        else:
            timeTuple = time.localtime(t)
            dstThen = timeTuple[-1]
            if dstNow != dstThen:
                if dstNow:
                    addend = 3600
                else:
                    addend = -3600
                timeTuple = time.localtime(t + addend)

        # 获得被回滚掉的日志名
        dfn = self.rotation_filename(self.baseFilename + "." +
                                     time.strftime(self.suffix, timeTuple))

        # TODO 在该处多进程会互相删除各自的日志文件
        if os.path.exists(dfn):
            os.remove(dfn)
        self.rotate(self.baseFilename, dfn)

        # 回滚计数器大于0
        if self.backupCount > 0:
            for s in self.getFilesToDelete():
                os.remove(s)
        if not self.delay:
            self.stream = self._open()
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval

        # 执行回滚
        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
           # ~~~ 省略 ~~~~

问题原因

问题出在上述代码的 36 ~ 38 行 ,此处并没有加锁,并不是进程安全的。

在日志需要回滚时,多进程的每个进程都调用了 doRollover() 方法,导致多进程抢占日志句柄,最终在日志回滚后只有一个进程能够正常将日志打印到日志文件中。

问题解决

对日志文件的文件描述符加独占锁(fcntl.LOCK_EX),防止多个进程同时同时获取到日志文件描述符。

具体修改代码如下: 该类中的 doRollover() 方法中文件覆盖的部分(上述代码 36 ~ 38 行)重写

class MultiCompatibleTimedRotatingFileHandler(TimedRotatingFileHandler):
    def __init__(self, log_path, interval, when, backupCount, encoding):
        super().__init__(filename=log_path, interval=interval, when=when, backupCount=backupCount, encoding=encoding)

    def doRollover(self):
        # ~~~ 省略,与上面代码保持一致 ~~~

        # if os.path.exists(dfn):
        #     os.remove(dfn)
        # self.rotate(self.baseFilename, dfn)

        # 多进程检测到旧句柄存在则不覆盖,继续向文件续写,避免多进程日志互相覆盖
        if not os.path.exists(dfn) and os.path.exists(self.baseFilename):
            f = open(self.baseFilename, 'a')
            fcntl.lockf(f.fileno(), fcntl.LOCK_EX)
            if not os.path.exists(dfn) and os.path.exists(self.baseFilename):
                os.rename(self.baseFilename, dfn)
            f.close()  # 释放锁 释放老 log 句柄

        # ~~~ 省略,与上面代码保持一致 ~~~
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
机器学习/深度学习 存储 算法
解锁文件共享软件背后基于 Python 的二叉搜索树算法密码
文件共享软件在数字化时代扮演着连接全球用户、促进知识与数据交流的重要角色。二叉搜索树作为一种高效的数据结构,通过有序存储和快速检索文件,极大提升了文件共享平台的性能。它依据文件名或时间戳等关键属性排序,支持高效插入、删除和查找操作,显著优化用户体验。本文还展示了用Python实现的简单二叉搜索树代码,帮助理解其工作原理,并展望了该算法在分布式计算和机器学习领域的未来应用前景。
|
11天前
|
API 开发工具 Python
|
13天前
|
存储 算法 文件存储
探秘文件共享服务之哈希表助力 Python 算法实现
在数字化时代,文件共享服务不可或缺。哈希表(散列表)通过键值对存储数据,利用哈希函数将键映射到特定位置,极大提升文件上传、下载和搜索效率。例如,在大型文件共享平台中,文件名等信息作为键,物理地址作为值存入哈希表,用户检索时快速定位文件,减少遍历时间。此外,哈希表还用于文件一致性校验,确保传输文件未被篡改。以Python代码示例展示基于哈希表的文件索引实现,模拟文件共享服务的文件索引构建与检索功能。哈希表及其分布式变体如一致性哈希算法,保障文件均匀分布和负载均衡,持续优化文件共享服务性能。
|
1月前
|
运维 应用服务中间件 nginx
docker运维查看指定应用log文件位置和名称
通过本文的方法,您可以更高效地管理和查看Docker容器中的日志文件,确保应用运行状态可控和可监测。
168 28
|
2月前
|
监控 网络安全 开发者
Python中的Paramiko与FTP文件夹及文件检测技巧
通过使用 Paramiko 和 FTP 库,开发者可以方便地检测远程服务器上的文件和文件夹是否存在。Paramiko 提供了通过 SSH 协议进行远程文件管理的能力,而 `ftplib` 则提供了通过 FTP 协议进行文件传输和管理的功能。通过理解和应用这些工具,您可以更加高效地管理和监控远程服务器上的文件系统。
65 20
|
27天前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
33 0
|
2月前
|
存储 数据采集 数据处理
如何在Python中高效地读写大型文件?
大家好,我是V哥。上一篇介绍了Python文件读写操作,今天聊聊如何高效处理大型文件。主要方法包括:逐行读取、分块读取、内存映射(mmap)、pandas分块处理CSV、numpy处理二进制文件、itertools迭代处理及linecache逐行读取。这些方法能有效节省内存,提升效率。关注威哥爱编程,学习更多Python技巧。
112 8
|
2月前
|
存储 JSON 对象存储
如何使用 Python 进行文件读写操作?
大家好,我是V哥。本文介绍Python中文件读写操作的方法,包括文件读取、写入、追加、二进制模式、JSON、CSV和Pandas模块的使用,以及对象序列化与反序列化。通过这些方法,你可以根据不同的文件类型和需求,灵活选择合适的方式进行操作。希望对正在学习Python的小伙伴们有所帮助。欢迎关注威哥爱编程,全栈路上我们并肩前行。
|
2月前
|
存储 算法 Serverless
剖析文件共享工具背后的Python哈希表算法奥秘
在数字化时代,文件共享工具不可或缺。哈希表算法通过将文件名或哈希值映射到存储位置,实现快速检索与高效管理。Python中的哈希表可用于创建简易文件索引,支持快速插入和查找文件路径。哈希表不仅提升了文件定位速度,还优化了存储管理和多节点数据一致性,确保文件共享工具高效运行,满足多用户并发需求,推动文件共享领域向更高效、便捷的方向发展。
|
3月前
|
计算机视觉 Python
如何使用Python将TS文件转换为MP4
本文介绍了如何使用Python和FFmpeg将TS文件转换为MP4文件。首先需要安装Python和FFmpeg,然后通过`subprocess`模块调用FFmpeg命令,实现文件格式的转换。代码示例展示了具体的操作步骤,包括检查文件存在性、构建FFmpeg命令和执行转换过程。
99 7

热门文章

最新文章