读取轮换日志文件和文件锁定

本文关键字:文件 锁定 文件锁 日志 读取 | 更新日期: 2023-09-27 18:32:44

我有一个Python服务将日志吐出到文本文件中。它每~400KB轮换一次。因此,Python 服务打开了文件的句柄,我们称之为 app.log。然后,它会不时地将内容写入文件,并再次将其刷新到磁盘。当它达到一定大小时,它会关闭其句柄,并将其移动到 app.log.1 并在 app.log 上启动新的句柄。

所以我无法更改此服务,但我有一个 C# 应用程序可以读取这些日志。我遇到了 3 种情况:

  • 如果我只是尝试使用 new FileStream(path, FileMode.Open); 读取这些日志,它不允许我,因为 Python 服务有一个句柄。
  • 如果我尝试使用 new FileStream(path, FileMode.Open, FileAccess.Read); 打开它,这允许我读取它,但如果服务尝试轮换日志,它将无法,因为我的 C# 应用程序现在对文件有一个句柄。
  • 如果我尝试使用 new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Delete); 打开文件,我的 Python 服务在删除文件时不会失败,但它将无法在应用程序上创建新句柄.log因为 C# 应用程序仍然有一个句柄。

我知道的唯一解决方案是使用 Windows 卷影复制 (VSS( 创建日志的快照,然后读取该快照,但这将非常昂贵,因为我们需要每 5 分钟查询一次日志。

另外,我对阅读轮换的日志,app.log.1,app.log.2等不感兴趣。

记录到 Windows 下的文本文件似乎对所有锁定/句柄都是一种痛苦。有人有什么建议吗?

读取轮换日志文件和文件锁定

您应该能够按照德米特里·波波夫(Dmitry Popov(在下面的回答中建议的那样打开文件,并且不会影响Python对文件的写入,但是这取决于Python应用程序对文件持有的锁,它可以完全锁定您,并且您无需采取任何措施来防止这种情况不入侵Windows。

FileSream fs = File.Open(@"c:'Test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)

以这种方式创建的 FileStream 对象在对其执行操作系统文件移动操作以重命名后,仍将连接到同一文件。

因此,假设您的python应用程序打开了一个名为Test.log的文件并开始写入该文件。您可以使用从上面行返回的文件流读取写入它的任何数据(在 python 刷新其缓冲区之后(。python应用程序每次都可以根据需要随时关闭和重新打开文件,并且读取应用程序将保持与该文件的连接。当 python 应用程序发出文件移动操作以将文件重命名为 Test1.log 时,上面返回的文件流仍将连接到现在称为 Test1 的文件.log因此您可以在启动新的日志文件之前继续读取文件末尾(如果需要(。对此有一个警告。Python 应用程序需要使用移动/重命名操作,而不是将文件复制到新文件并删除旧文件,但如果这是它所做的,我会感到惊讶。

在写入应用程序完成读取文件之前,您的阅读应用程序可能会到达文件的末尾。在这种情况下,fs。读取将在超时后继续返回 0,直到写入应用程序打开文件并写入更多文件。如果需要,您可以使超时很长/无限。

由于您不想在开始新文件之前读取一个文件的末尾,因此您可以定期关闭并重新打开该文件。不带数字后缀的日志文件应始终是最新的。

但是,如果您希望读取应用程序先读取到一个日志文件的末尾,然后再开始下一个日志文件的开头,则需要确定写入应用程序何时完成对日志文件的写入。它还需要找出文件现在叫什么,以便它接下来可以读取 n-1。是否有一些由 python 应用程序编写的标记,您可以寻找来表示文件的结尾?它写的是"日志结束"还是类似的东西?

还要注意,在短时间内,日志文件 n-1 将不存在。这是因为如果您有日志文件 0、1、2 和 3,则需要将日志文件 3 转换为日志文件 4,然后才能将日志文件 2 转换为日志文件 3。执行此操作时,会有一小段时间,当您有日志文件 0、1、2、4 和没有 3 时。

就我个人而言,我会发现为您的 Python 应用程序编写日志记录的开发人员首先会给他/她造成这种头痛的邪恶之眼。最新的日志文件数量最多有什么问题?

using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
  //Do works
}

在这种情况下,C# 线程不会锁定文件,Python 脚本可以写入并关闭文件以创建另一个文件而不会死锁。

您可以组合FileShare标志:

FileShare.Write | FileShare.Delete

下面是一个演示:

using (var cSharp = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Write | FileShare.Delete))
{
    // The Python service will be able to change and to rename the file:
    using (var python = new FileStream(filename, FileMode.Open, FileAccess.Write, FileShare.Read))
    {
    }
    File.Move(filename, newFilename);
}

您将不得不处理并发问题。您可以使用 FileSystemWatcher 来监视文件更改。