65.9K
CodeProject 正在变化。 阅读更多。
Home

日志监视器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (19投票s)

2007 年 6 月 5 日

4分钟阅读

viewsIcon

129303

downloadIcon

6464

本文介绍了如何编写一个应用程序来监视文本日志文件。

Screenshot - LogMonitorScreen.jpg

引言

我经常需要监视基于文本的日志文件,例如服务或 IIS 日志。 我厌倦了必须在我的编辑器中刷新文件,或者必须关闭并重新打开文件才能看到最新的更改。 因此,我编写了一个工具,它可以为我完成这项工作,并不断向我显示日志文件的最新添加内容。 该应用程序功能相当全面。 它支持以下功能

  • 可以同时打开多个日志文件。
  • 文件名可以作为参数传递给应用程序,以便在启动时加载。
  • 使用 TabControl 在正在监视的日志文件之间切换。
  • 文件可以拖放到应用程序上。
  • 日志可以通过计时器或立即使用 FileSystemWatcher 进行更新。
  • 更新可以暂停。
  • 为了优化性能,只读取自上次更新以来添加到日志中的行。
  • 如果文件太大,则仅读取日志的末尾。
  • 正确处理仅使用换行符终止行的日志文件。
  • 设置和窗口位置保存在配置文件中。

使用代码

要使用源代码,只需解压缩 源文件 并打开 LogMonitor.sln 解决方案文件。 这是完全使用 C# 在 .NET 2.0 中编写的。

关注点

LogMonitorControl 用户控件

此应用程序中的主要工作类是 LogMonitorControl,它是一个用户控件,用于监视单个日志文件。 TabControl 用于保存多个 LogMonitorControl 实例。

线程问题

一个棘手的问题是 LogMonitorControl 可以使用 TimerFileSystemWatcher 来监视日志文件。 它们在线程方面表现不同。 使用 Timer 时,计时器滴答回调发生在主线程上。 但是,当使用 FileSystemWatcher 方法时,回调发生在另一个线程上。 因此,更新子控件涉及通过 InvokeRequired 属性检查是否应使用 Invoke。 匿名委托对此有很大帮助。 但是,如果要为控件执行大量代码,那么编写一个执行这些操作的方法也很方便。 然后或者 Invoke 该方法,或者直接调用它。 下面是一个需要它的示例。 请注意,方法 DoScrollToEndLogMonitorControl 类的 一个方法,而不是控件的一个成员 调用方法

public void ScrollToEnd()
{
    if (_textBoxContents.InvokeRequired)
    {
        _textBoxContents.Invoke(new MethodInvoker(DoScrollToEnd));
    }
    else
    {
        DoScrollToEnd();
    }
}

拖放支持
(嗯,只是拖放支持而已。)

添加拖放支持非常容易,只包含 2 个方法。 代码如下

private void tabControl_DragDrop(object sender, DragEventArgs e)
{
    // Accept files dropped and create new a tab each file.
    IDataObject data = e.Data;
    // Only accept file drops
    if (data.GetDataPresent(DataFormats.FileDrop))
    {
        // Get the list of files dropped and create tabs for each
        string[] filesToDrop = 
            (string[])e.Data.GetData(DataFormats.FileDrop, false);
        for (int idx = 0; idx < filesToDrop.Length; idx++)
        {
            MonitorNewFile(filesToDrop[idx]);
        }
    }
}

private void tabControl_DragEnter(object sender, DragEventArgs e)
{
    IDataObject data = e.Data;
    // Only accept file drops
    if (data.GetDataPresent(DataFormats.FileDrop))
    {
        e.Effect = DragDropEffects.Copy;
    }
    else
    {
        e.Effect = DragDropEffects.None;
    }
}

FileSystemWatcher

FileSystemWatcher 类位于 System.IO 命名空间中,用于监视对文件或目录中任何文件的更改。 构造函数接受路径和文件名模式。 LogMonitor 应用程序使用文件名作为模式调用构造函数,以监视单个文件。 我们对该类可以引发的所有更改事件感兴趣,因此我们注册了所有这些事件。 我们实际上可以通过重命名来跟踪此文件,并继续监视日志文件,但我选择简单地继续监视相同的文件。 然后,如果文件被重命名回去,我们将检测到这一点。 同样,如果使用相同的文件名创建另一个文件,也将被检测到。 关于 FileSystemWatcher 类,有一件事并不完全明显。 也就是说,如果您注册了文件事件,除非将属性 EnableRaisingEvents 设置为 true,否则您将永远不会收到它们。

_watcher = new System.IO.FileSystemWatcher(path, baseName);
FileSystemEventHandler handler = new FileSystemEventHandler(watcher_Changed);
_watcher.Changed += handler;
_watcher.Created += handler;
_watcher.Deleted += handler;
_watcher.Renamed += watcher_Renamed;
// Without setting EnableRaisingEvents nothing happens
_watcher.EnableRaisingEvents = true;

以最小的锁定打开文件

某些应用程序在执行期间保持日志文件打开状态,例如 IIS。 为了使 LogMonitor 能够最好地使用这些类型的应用程序,它使用可能最友好的设置打开日志文件。 在 .NET 中读取文本文件的最简单方法是使用 File.ReadAllText() 方法。 但是,这并不能使用正确的共享标志打开文件。 因此,我使用 File.Open 打开文件并指定访问和共享模式。 然后打开一个 StreamReader 并在该流上使用 ReadToEnd。 以下是用于读取文件的代码,其中一些与此无关的行已被删除

string newFileLines = "";
using (FileStream stream = File.Open(_fileName, 
    FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (StreamReader reader = new StreamReader(stream))
    {
        newFileLines = reader.ReadToEnd();
    }
}

历史

  • 2007 年 6 月 5 日 - 发布原始版本
  • 2007 年 6 月 6 日 - 版本 1.0.0.1
    • 修复了大型文件修剪文本的错误。(感谢 s_mom 的捕捉)。
© . All rights reserved.