我有一个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服务在删除文件时不会失败,但是在 app.log 上创建新句柄会失败C#应用程序仍然可以处理它。我所知道的唯一解决方案是使用Windows Shadow Copy(VSS)创建日志快照,然后读取该快照,但这将非常昂贵,因为我们需要每隔5分钟查询一次日志
另外,我对阅读旋转的日志感兴趣, app.log.1 , app.log.2 等。
在Windows下记录文本文件似乎是所有锁定/句柄的痛苦。有没有人有任何建议?
答案 0 :(得分:3)
你应该能够像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.Read将在超时后继续返回0,直到写入应用程序打开文件并写入更多内容。如果你愿意的话,你可以把时间做得很长/无限。
由于您不想在启动新文件之前读取到一个文件的末尾,因此您可以关闭并定期重新打开该文件。没有数字后缀的日志文件应该始终是最新的。
但是,如果您希望读取应用程序在下一个日志文件开头之前读取到一个日志文件的末尾,则需要在写入应用程序写入日志文件时解决。此外,它需要找出现在调用的文件,以便下次读取n-1。是否有一些由python应用程序编写的标记,你可以寻找它来表示文件的结尾?它写的是“日志结束”还是类似的东西?
还要注意,当LogFile n-1不存在时,会有很短的时间。这是因为如果你有日志文件0,1,2和3,它需要将日志文件3放入日志文件4,然后才能将日志文件2放入日志文件3.虽然这样做会有一段短暂的时间有日志文件0,1,2,4和3的时间。
就我个人而言,我会发现为Python应用程序编写日志记录的开发人员首先会给他/她造成这种头痛的恶意。使最新的日志文件具有最大数量有什么问题?
答案 1 :(得分:1)
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
//Do works
}
在这种情况下,C#线程不会锁定文件,您的Python脚本可以编写并关闭该文件以创建另一个没有死锁的文件。
答案 2 :(得分:0)
您可以合并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来监控文件更改。