【问题标题】:Using MemoryMappedFile and FileSystemWatcher to detect new entries to logfile使用 MemoryMappedFile 和 FileSystemWatcher 检测日志文件的新条目
【发布时间】:2010-11-16 20:38:33
【问题描述】:

我有一个由第 3 方应用程序编写的日志文件,我希望我的应用程序实时/近乎实时地“读取”该日志文件,解析新的日志条目并对某些事件采取行动。

我的想法是,我可以结合使用 FileSystemWatcher(发出文件更改信号)和 MemoryMappedFile(从某个偏移量继续读取)来实现这一点。

但是,由于这是我第一次使用 MemoryMappedFiles,我确实遇到了一些问题,这可能是由于没有正确理解这个概念而引起的(例如,我无法打开现有文件,因为它正在被其他进程使用)。

我想知道是否有人有一个如何使用 MemoryMappedFiles 读取被另一个进程锁定的文件的示例?

谢谢,

汤姆

编辑:

从 cmets 看来,内存映射文件无法帮助我访问具有排他锁的文件。然而,“尾巴”工具,例如, Baretail (http://www.baremetalsoft.com/baretail/index.php) 能够做到这一点。以 1s 的间隔从另一个应用程序读取具有排他锁的文件没有问题)。那么,一定有办法做到这一点?

编辑编辑

要回答我自己的问题,打开锁定文件的技巧是使用以下访问标志创建 FileStream:

fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);

【问题讨论】:

  • 我在 James 的帖子中对所需的 FileShare 参数发表了评论。 MMF 无法解决完全锁定的文件,如果是这种情况,您会被卡住。
  • 请看我上面的编辑。与 MMF 与 FileStream 现在相比,这个问题似乎更侧重于绕过排他锁。我已经尝试了多个 Windows 尾部工具,它们似乎都能够很好地解决独占锁,知道它们是如何实现的吗?

标签: c# .net memory-mapped-files


【解决方案1】:

要回答我自己的问题,读取锁定文件的技巧是使用以下访问标志创建 FileStream:

FileStream fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);

现在只需进行基于间隔的轮询或查找 FileSystemWatcher 更改事件以检测文件更改

【讨论】:

  • 你使用过 MemoryMappedFile 吗?我正在寻找实现同样的东西。
【解决方案2】:

我不确定 MemoryMappedFiles 是否会帮助您。看看 FileStream:

var stream = new FileStream(path, FileMode.Open, FileShare.Read);
stream.Seek(offset, SeekOrigin.Begin);

虽然如果第 3 方应用程序将文件以独占方式锁定,但您无能为力...

【讨论】:

  • 好吧,使用 FileStream,我无法读取被另一个进程独占锁定的文件,我希望我可以使用内存映射文件来实现这一点。像 baretail (baremetalsoft.com/baretail/index.php) 这样的 Windows 尾部工具能够很好地读取锁定的文件,所以必须有一些方法来实现这一点
  • FileShare.Read 不起作用,写入日志文件的应用程序已经获得了写入权限。您不能否认已经获得的权利。大概也是OP的问题。您必须在此处指定 ReadWrite。
【解决方案3】:

[开始第二次编辑]

还有一个想法……

如果第三方应用碰巧使用 NLog、log4net 或 System.Diagnostics 等日志框架,您仍然可以编写自己的 Target/Appender/TraceListener 并将消息路由到您可以查看它们的位置(例如不是以独占方式打开的文件,其他进程等)。

如果您的第三方应用正在使用日志框架,我们现在可能已经听说过它;-)

[结束第二次编辑]

[开始编辑]

我想我误读了这个问题。乍一看,您好像在使用已实现日志记录的第三方库,并且您想在生成日志记录的程序中进行此解析。重新阅读您的问题后,听起来您想从应用程序外部“收听”日志文件。如果是这样的话,我的回答可能对你没有帮助。对不起。

[结束编辑]

我没有任何关于 MemoryMappedFiles 的信息,但我想知道您是否可以通过为 3rd 方日志记录系统编写自定义侦听器/目标/附加程序来实现您所追求的目标?

例如,如果您使用的是 NLog,您可以编写一个自定义目标并将所有日志消息定向到那里(同时也将它们定向到“真实”目标)。这样,您可以在记录每条日志消息时对其进行破解(因此它实际上是实时的,而不是接近实时的)。你可以用 log4net 和 System.Diagnostics 做同样的事情。

请注意,NLog 甚至还有一个“MethodCall”目标。要使用该方法,您只需编写一个具有正确签名的静态方法。不知道log4net有没有和这个类似的概念。

这似乎比尝试读取和解析由第三方软件编写的日志文件更容易可靠地工作。

【讨论】:

    【解决方案4】:

    如果文件“正在使用”,则对此无能为力。它确实在“使用中”。 MemoryMappedFiles 用于从驱动器读取大量数据或与其他程序共享数据。这无助于绕过“使用中”的限制。

    【讨论】:

      【解决方案5】:

      Memorymapped 文件与初始化它的 FileStream 具有相同的限制,请确保像这样初始化 Memory-Mapped-File

      var readerStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

      var mmf = MemoryMappedFile.CreateFromFile(readerStream, null, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, false);

      如果其他进程已经完全锁定了它,即使你写不出来,你也很倒霉,不确定是否有办法解决这个问题。也许使用一些计时器来检测进程何时停止写入。

      【讨论】:

        【解决方案6】:

        我已经做了类似的事情,只是为了在控制台上监控日志文件(而不是处理),但原理是一样的。和你一样,我使用 FileSystemWatcher,重要的逻辑在我的 OnChanged 事件处理程序中:

        case WatcherChangeTypes.Changed:
        {
            System.IO.FileInfo fi = new FileInfo(e.FullPath);
        
            long prevLength;
        
            if (lastPositions.TryGetValue(e.FullPath, out prevLength))
            {
                using (System.IO.FileStream fs = new FileStream(
                   e.FullPath, FileMode.Open, FileAccess.Read))
                {
                    fs.Seek(prevLength, SeekOrigin.Begin);
                    DumpNewData(fs, (int)(fi.Length - prevLength));
                    lastPositions[e.FullPath] = fs.Position;
                }
            }
            else
              lastPositions.Add(e.FullPath, fi.Length);
        
            break;
        }
        

        lastPositions 在哪里

        Dictionary<string, Int64> lastPositions = new Dictionary<string, long>();
        

        而 DumpNewData 很简单

        private static void DumpNewData(FileStream fs, int bytesToRead)
        {
            byte[] bytesRead = new byte[bytesToRead];
            fs.Read(bytesRead, 0, bytesToRead);
            string s = System.Text.ASCIIEncoding.ASCII.GetString(bytesRead);
            Console.Write(s);
        }
        

        【讨论】:

        • 不幸的是,这并没有解决读取具有排他锁的文件的问题
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-21
        • 2018-12-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多