Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to monitor Textfile and continuously output content in a textbox?

Tags:

c#

I'm making a program that controls a game server. One of the functions I'm making, is a live server logfile monitor.

There is a logfile (a simple textfile) that gets updated by the server as it runs.

How do I continuously check the logfile and output it's content in a RichTextBox?

I did this simple function just try and get the content of the log. It will of course just get the text row by row and output it to my textbox. Also it will lock the program for as long as the loop runs, so I know it's useless.

public void ReadLog()
{
  using (StreamReader reader = new StreamReader("server.log"))
  {
    String line;
        
    // Read and display lines from the file until the end of the file is reached.
    while ((line = reader.ReadLine()) != null)
    {
      monitorTextBox.AppendText(line + "\n");
      CursorDown();
    }
  }
}

But how would you go about solving the live monitoring as simple as possible?

*** EDIT ***

I'm using Prescots solution. great stuff.

At the moment I'm using a sstreamreader to put the text from the file to my textbox. I ran into the problem is that, whenever I tried to access any of the gui controls in my event handler the program just stopped with no error or warnings.

I found out that it has to do with threading. I solved that like this:

private void OnChanged(object source, FileSystemEventArgs e)
{
    if (monitorTextField.InvokeRequired)
    {
        monitorTextField.Invoke((MethodInvoker)delegate { OnChanged(source, e); });
    }
    else
    {
      StreamReader reader = new StreamReader("file.txt");

      monitorTextField.Text = "";
      monitorTextField.Text = reader.ReadToEnd();
      reader.Close();
      CursorDown();
    }
}

Now my only problem is that the file.txt is used by the server so I can't access it, since it's "being used by another process". I can't control that process, so maybe I'm out of luck.

But the file can be opened in notepad while the server is running, so somehow it must be possible. Perhaps I can do a temp copy of the file when it updates and read the copy. I don't know.

like image 994
Christoffer Avatar asked Mar 18 '12 08:03

Christoffer


2 Answers

Check out the System.IO.FileSystemWatcher class:

public static Watch() 
{
    var watch = new FileSystemWatcher();
    watch.Path = @"D:\tmp";
    watch.Filter = "file.txt";
    watch.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite; //more options
    watch.Changed += new FileSystemEventHandler(OnChanged);
    watch.EnableRaisingEvents = true;
}

/// Functions:
private static void OnChanged(object source, FileSystemEventArgs e)
{
    if(e.FullPath == @"D:\tmp\file.txt")
    {
        // do stuff
    }
}

Edit: if you know some details about the file, you could handle the most efficent way to get the last line. For example, maybe when you read the file, you can wipe out what you've read, so next time it's updated, you just grab whatever is there and output. Perhaps you know one line is added at a time, then your code can immediately jump to the last line of the file. Etc.

like image 129
Prescott Avatar answered Oct 01 '22 14:10

Prescott


Although the FileSystemWatcher is the most simple solution I have found it to be unreliable in reality.. often a file can be updated with new contents but the FileSystemWatcher does not fire an event until seconds later and often never.

The only reliable way I have found to approach this is to check for changes to the file on a regular basis using a System.Timers.Timer object and checking the file size.

I have written a small class that demonstrates this available here:

https://gist.github.com/ant-fx/989dd86a1ace38a9ac58

Example Usage

var monitor = new LogFileMonitor("c:\temp\app.log", "\r\n");

monitor.OnLine += (s, e) =>
{
    // WARNING.. this will be a different thread...
    Console.WriteLine(e.Line);
};

monitor.Start();

The only real disadvantage here (apart from a slight performance delay caused by file size checking) is that because it uses a System.Timers.Timer the callback comes from a different thread.

If you are using a Windows Forms or WPF app you could easily modify the class to accept a SynchronizingObject which would ensure the event handler events are called from the same thread.

like image 34
antfx Avatar answered Oct 01 '22 12:10

antfx