C# 使用MemoryMappedFile和FileSystemWatcher检测日志文件的新条目

C# 使用MemoryMappedFile和FileSystemWatcher检测日志文件的新条目,c#,.net,memory-mapped-files,C#,.net,Memory Mapped Files,我有一个由第三方应用程序编写的日志文件,我希望我的应用程序能够实时/近时间“读取”该日志文件,解析新的日志条目并对某些事件采取行动 我的想法是,我可以通过组合使用FileSystemWatcher(发出文件更改信号)和MemoryMappedFile(继续从某个偏移量读取)来实现这一点 但是,由于这是我第一次使用MemoryMappedFile,我确实遇到了一些问题,这些问题可能是由于没有正确理解该概念而引起的(例如,由于其他进程正在使用现有文件,我无法打开该文件) 我想知道是否有人举了一个例子

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

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

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

我想知道是否有人举了一个例子,说明如何使用MemoryMappedFiles来读取被另一个进程锁定的文件

谢谢

汤姆

编辑:

从评论来看,内存映射文件似乎无法帮助我访问具有独占锁的文件。但是,“尾巴”工具,例如Baretail(http://www.baremetalsoft.com/baretail/index.php)我们能够做到这一点。从另一个应用程序读取具有独占锁的文件(间隔1s)没有问题。所以,一定有办法做到这一点

编辑编辑


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

fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);
FileStream fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);
[开始第二次编辑]

还有一个想法

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

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

[第二次编辑结束]

[开始编辑]

我想我误解了这个问题。一开始听起来好像您使用的是第三方库,该库实现了日志记录,您希望从生成日志记录的程序中进行解析。重读您的问题后,听起来您想从应用程序外部“侦听”日志文件。如果是这样的话,我的回答可能帮不了你。对不起

[结束编辑]

关于MemoryMappedFile,我没有什么可以提供的,但我想知道您是否可以通过为第三方日志记录系统编写一个自定义侦听器/目标/附加器来实现您所追求的目标

例如,如果您使用的是NLog,您可以编写一个自定义目标并将所有日志消息定向到该目标(同时也将它们定向到“真实”目标)。通过这种方式,您可以在记录每个日志消息时对其进行破解(因此它实际上是实时的,而不是接近实时的)。您可以对log4net和System.Diagnostics执行相同的操作

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


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

我不确定MemoryMappedFile是否能帮助您。请看一下FileStream:

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

尽管如果第三方应用程序以独占方式锁定了文件,您对此无能为力……

如果文件“正在使用”,则对此无法采取任何措施。它确实在“使用中”。MemoryAppedFile用于从驱动器读取大量数据或与其他程序共享数据。这无助于绕过“正在使用”限制。

内存映射文件与初始化它的文件流受相同的限制,请确保像这样初始化内存映射文件

var readerStream=newfilestream(路径,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)

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


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

我做过类似的事情,只是为了监视控制台上的日志文件(与处理相反),但原理是相同的。与您一样,我使用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;
}
最后的位置在哪里

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

为了回答我自己的问题,读取锁定文件的诀窍是使用以下访问标志创建FileStream:

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

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

好吧,使用FileStream,我无法读取被另一个进程独占锁定的文件,我希望通过内存映射文件可以实现这一点。不过,像baretail()这样的Windows tail工具能够很好地读取锁定的文件,因此必须有某种方法来实现此文件共享。读取不起作用,写入日志文件的应用程序已经获得了写入权限。你不能否认已经获得的权利。大概也是OP的问题。您必须在这里指定ReadWrite。我在James的帖子中对必需的FileShare参数做了评论。MMF无法处理完全锁定的文件,如果是这种情况,您将被卡住。请参阅上面的编辑。现在的问题似乎更关注于绕过独占锁,而不是MMF与FileStream。我尝试过多个windows tail工具,它们似乎都能够解决独占锁问题,你知道它们是如何实现的吗?不幸的是,这并不能解决使用独占锁读取文件的问题,你是否使用了MemoryMappedFile?我希望实现同样的事情。