1

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

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

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

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

谢谢,

汤姆

编辑:

从评论来看,内存映射文件似乎无法帮助我访问具有独占锁的文件。然而,像 Baretail (http://www.baremetalsoft.com/baretail/index.php) 这样的“tail”工具能够做到这一点。以 1s 的间隔从另一个应用程序读取具有排他锁的文件没有问题)。那么,必须有某种方法来做到这一点?

编辑编辑

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

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

6 回答 6

2

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

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

Now it's just a matter of either doing interval based polling or looking for FileSystemWatcher change events to detect file changes

于 2010-11-16T21:47:24.630 回答
0

[开始第二次编辑]

还有一个想法...

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

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

[结束第二次编辑]

[开始编辑]

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

[结束编辑]

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

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

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

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

于 2010-11-16T20:48:18.750 回答
0

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

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

尽管如果第 3 方应用程序已将文件独占锁定,那么您对此无能为力...

于 2010-11-16T20:50:15.647 回答
0

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

于 2010-11-16T21:06:48.050 回答
0

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

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

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

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

于 2010-11-16T21:22:29.607 回答
0

我已经做了类似的事情,只是为了在控制台上监控日志文件(而不是处理),但原理是一样的。和你一样,我使用 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);
}
于 2010-11-16T21:23:13.273 回答