2

环境:欢迎任何 .Net 框架。我有一个被写入 24/7 的日志文件。

我正在尝试创建一个将读取日志文件并处理数据的应用程序。

有效读取日志文件的最佳方法是什么?我想用 FileSystemWatcher 之类的东西监视文件。但是,如何确保在我的应用程序处理数据后不会读取相同的数据?或者说应用程序由于某种未知原因中止,它将如何从上次中断的地方继续?

日志文件中的有效负载周围通常有一个页眉和页脚。也许内容中还有一个 id 字段。虽然还不确定 id 字段是否存在。

我还想象可能将读取的行数保存在某处,以将其用作书签。

4

4 回答 4

1

它记录到文件是否有原因?文件很棒,因为它们易于使用,并且作为最小的公分母,出错的可能性相对较小。但是,文件是有限的。正如您所说,当您读取文件时,不能保证对文件的写入会完成。写入日志的多个应用程序可能会相互干扰。没有简单的排序或过滤机制。日志文件可能会很快增长得非常大,并且没有简单的方法可以将旧事件(例如超过 24 小时的事件)移动到单独的文件中以进行备份和保留。

相反,我会考虑将日志写入数据库。表结构可以非常简单,但您可以获得事务的优势(因此您可以轻松提取或备份)并使用几乎普遍理解的语法进行搜索、排序和过滤。如果您担心负载峰值,请使用消息队列,例如用于 SQL Server 的http://msdn.microsoft.com/en-us/library/ms190495.aspx 。

为了使转换更容易,请考虑使用log4net之类的日志框架。它将大部分内容从您的代码中抽象出来。

另一种选择是使用syslog之类的系统,或者,如果您有多个服务器和大量日志,请使用flume。通过将日志文件移出源计算机,您可以更有效地在不同的计算机上存储或检查它们。但是,对于您当前的问题,这些可能是矫枉过正。

于 2012-09-06T04:37:17.693 回答
1

出于显而易见的原因,读取文件的全部内容以及从日志文件中删除行(在将它们加载到您的应用程序之后)是不可能的。

我能想到的部分解决方案是拥有一个小型数据库(可能比成熟的 MySQL/MS SQL/PostgreSQL 实例小得多)并使用从日志文件中读取的内容填充表。我很确定即使断电然后机器再次启动,大多数关系数据库应该能够轻松恢复它的状态。此解决方案需要一些可用于识别日志文件中的行的数据(例如:记录操作的确切时间、发生操作的机器等)

于 2012-09-06T04:04:16.327 回答
1

好吧,你必须自己找出你的特殊情况的魔法。如果您要使用众所周知的文本编码,它可能非常简单。查看 System.IO.StreamReader 和它的 ReadLine()、DiscardBufferedData() 方法和 BaseStream 属性。您应该能够记住您在文件中的最后一个位置并稍后回到该位置并再次开始阅读,因为您确定该文件只是附加的。不过,还有其他事情需要考虑,对此没有统一的通用答案。

就像一个天真的例子(您可能仍然需要进行很多调整才能使其正常工作):

    static void Main(string[] args)
    {
        string filePath = @"c:\log.txt";
        using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            using (var streamReader = new StreamReader(stream,Encoding.Unicode))
            {
                long pos = 0;
                if (File.Exists(@"c:\log.txt.lastposition"))
                {
                    string strPos = File.ReadAllText(@"c:\log.txt.lastposition");
                    pos = Convert.ToInt64(strPos);
                }
                streamReader.BaseStream.Seek(pos, SeekOrigin.Begin); // rewind to last set position.
                streamReader.DiscardBufferedData(); // clearing buffer
                for(;;)
                {
                    string line = streamReader.ReadLine();
                    if( line==null) break;

                    ProcessLine(line);
                }
                // pretty sure when everything is read position is at the end of file.
                File.WriteAllText(@"c:\log.txt.lastposition",streamReader.BaseStream.Position.ToString());
            }
        }
    }
于 2012-09-06T03:43:00.840 回答
1

我认为您会发现 File.ReadLines(filename) 函数与 LINQ 结合使用对于这样的事情非常方便。ReadAllLines() 会将整个文本文件作为 string[] 数组加载到内存中,但 ReadLines 将允许您在遍历文件时立即开始枚举行。这不仅可以节省您的时间,而且可以将内存使用率保持在非常低的水平,因为它一次处理每一行。使用语句很重要,因为如果该程序被中断,它将关闭文件流,刷新写入器并将未写入的内容保存到文件中。然后,当它启动时,它将跳过所有已读取的文件。

int readCount = File.ReadLines("readLogs.txt").Count();
using (FileStream readLogs = new FileStream("readLogs.txt", FileMode.Append))
using (StreamWriter writer = new StreamWriter(readLogs))
{
     IEnumerable<string> lines = File.ReadLines(bigLogFile.txt).Skip(readCount);
     foreach (string line in lines)
     {
         // do something with line or batch them if you need more than one
         writer.WriteLine(line);
     }
}

正如 MaciekTalaska 提到的,如果这是 24/7 写入的内容并且会变得非常大,我强烈建议使用数据库。文件系统根本不具备处理这种容量的能力,您将花费大量时间来尝试发明数据库可以轻而易举地完成它的解决方案。

于 2012-09-06T04:18:34.027 回答