Update: Code has been moved to GitHub
Introduction
As a member of an IT Helpdesk team, I am constantly trawling through logs trying to find out "what went wrong?" This is especially time-consuming when troubleshooting issues with Windows Services and background applications. With some applications and services producing large (50MB+) log files, I often find myself moving or renaming the old log files so I can get a small, easy-to-read file that displays the most recent entries since the service/application was restarted.
This tool eliminates these problems by monitoring the file and displaying any text that is written to it on-the-fly. It uses a custom WinForms TabPage
control, called LogTabPage
with a nested LogWatcher
class that inherits FileSystemWatcher
. The FileSystemWatcher
class provides the functions for monitoring the file for changes and I simply use a FileStream
/StreamReader
combination to read the text and append it to a TextBox
.
LogTabPage Class
The LogTabPage
class is a custom TabPage
that contains a single RichTextBox
and a nested LogWatcher
class to monitor the log file. It contains code for creating the LogWatcher
and appending text to the RichTextBox
.
class LogTabPage : TabPage
{
internal RichTextBox TextBox = new RichTextBox();
internal LogWatcher Watcher;
public LogTabPage(string FileName, string Suffix)
{
this.Text = Path.GetFileName(string.Format("{0} {1}", FileName, Suffix));
TextBox.Dock = DockStyle.Fill;
TextBox.BackColor = Color.White;
TextBox.ReadOnly = true;
this.Controls.Add(TextBox);
CreateWatcher(FileName);
}
private void CreateWatcher(string FileName)
{
Watcher = new LogWatcher(FileName);
Watcher.Path = Path.GetDirectoryName(FileName);
Watcher.NotifyFilter = (NotifyFilters.LastWrite | NotifyFilters.Size);
Watcher.Filter = Path.GetFileName(FileName);
Watcher.TextChanged += new LogWatcher.LogWatcherEventHandler(Watcher_Changed);
Watcher.EnableRaisingEvents = true;
}
void Watcher_Changed(object sender, LogWatcherEventArgs e)
{
if (TextBox.InvokeRequired) this.Invoke(new Action(delegate() { AppendText(e.Contents); }));
else AppendText(e.Contents);
}
private void AppendText(string Text)
{
TextBox.Text += Text;
if (!MainForm.Frozen)
{
TextBox.SelectionStart = TextBox.Text.Length;
TextBox.SelectionLength = 0;
TextBox.ScrollToCaret();
}
}
LogWatcher Class
The LogWatcher
class is an extension of the FileSystemWatcher
class that includes methods for reading the text from the file and an event for when the text is changed.
internal class LogWatcher : FileSystemWatcher
{
internal string FileName;
FileStream Stream;
StreamReader Reader;
public LogWatcher(string FileName)
{
this.Changed += OnChanged;
this.FileName = FileName;
Stream = new System.IO.FileStream
(FileName,FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Reader = new System.IO.StreamReader(Stream);
Stream.Position = Stream.Length;
}
public void OnChanged(object o, FileSystemEventArgs e)
{
string Contents = Reader.ReadToEnd();
LogWatcherEventArgs args = new LogWatcherEventArgs(Contents);
if (TextChanged != null) TextChanged(this, args);
}
public delegate void LogWatcherEventHandler(object sender, LogWatcherEventArgs e);
public event LogWatcherEventHandler TextChanged;
}
History
I originally created the application in VB.NET with little knowledge of .NET. I have since re-written it in C#.
Update: An updated version (4.0) is now available. Extra features include support for different Encodings and Fonts as requested. Links have been provided to both the executable and the source. This is a major rewrite using C# and WPF. The code in version 4.0 is very different to the code posted in the tip.