Skip to content

Instantly share code, notes, and snippets.

@mikedavies-dev
Created May 27, 2015 09:40
Show Gist options
  • Save mikedavies-dev/989dd86a1ace38a9ac58 to your computer and use it in GitHub Desktop.
Save mikedavies-dev/989dd86a1ace38a9ac58 to your computer and use it in GitHub Desktop.
A simple log file monitor class for .NET
/*
A simple log file monitor class for .NET
Uses a threaded timer to check for changes in the file, if the file length has changed then the unread
section of the file is read and parsed into lines before being passed back to the event handler.
Note, because the class uses the threaded timer callbacks on the event hander WILL be made form a
different thread, keep this in mind when using the class.
This class is more reliable than the FileSystemWatcher class provided by Microsoft which often does not
fire an event even after the file size changes
TODO:
* Customer timer period
* Option to ignore exsiting file contents
* Option to provide SynchronizingObject
Sample 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();
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Timers;
namespace antfx
{
public class LogFileMonitorLineEventArgs : EventArgs
{
public string Line { get; set; }
}
public class LogFileMonitor
{
public EventHandler<LogFileMonitorLineEventArgs> OnLine;
// file path + delimiter internals
string _path = String.Empty;
string _delimiter = String.Empty;
// timer object
Timer _t = null;
// buffer for storing data at the end of the file that does not yet have a delimiter
string _buffer = String.Empty;
// get the current size
long _currentSize = 0;
// are we currently checking the log (stops the timer going in more than once)
bool _isCheckingLog = false;
protected bool StartCheckingLog()
{
lock (_t)
{
if (_isCheckingLog)
return true;
_isCheckingLog = true;
return false;
}
}
protected void DoneCheckingLog()
{
lock (_t)
_isCheckingLog = false;
}
public LogFileMonitor(string path, string delimiter = "\n")
{
_path = path;
_delimiter = delimiter;
}
public void Start()
{
// get the current size
_currentSize = new FileInfo(_path).Length;
// start the timer
_t = new Timer();
_t.Elapsed += CheckLog;
_t.AutoReset = true;
_t.Start();
}
private void CheckLog(object s, ElapsedEventArgs e)
{
if (StartCheckingLog())
{
try
{
// get the new size
var newSize = new FileInfo(_path).Length;
// if they are the same then continue.. if the current size is bigger than the new size continue
if (_currentSize >= newSize)
return;
// read the contents of the file
using (var stream = File.Open(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader sr = new StreamReader(stream))
{
// seek to the current file position
sr.BaseStream.Seek(_currentSize, SeekOrigin.Begin);
// read from current position to the end of the file
var newData = _buffer + sr.ReadToEnd();
// if we don't end with a delimiter we need to store some data in the buffer for next time
if (!newData.EndsWith(_delimiter))
{
// we don't have any lines to process so save in the buffer for next time
if (newData.IndexOf(_delimiter) == -1)
{
_buffer += newData;
newData = String.Empty;
}
else
{
// we have at least one line so store the last section (without lines) in the buffer
var pos = newData.LastIndexOf(_delimiter) + _delimiter.Length;
_buffer = newData.Substring(pos);
newData = newData.Substring(0, pos);
}
}
// split the data into lines
var lines = newData.Split(new string[] { _delimiter }, StringSplitOptions.RemoveEmptyEntries);
// send back to caller, NOTE: this is done from a different thread!
foreach (var line in lines)
{
if (OnLine != null)
OnLine(this, new LogFileMonitorLineEventArgs { Line = line });
}
}
// set the new current position
_currentSize = newSize;
}
catch (Exception)
{
}
// we done..
DoneCheckingLog();
}
}
public void Stop()
{
if (_t == null)
return;
_t.Stop();
}
}
}
@yogendra581
Copy link

Just seen the code, it is really nice.

@calebmauer
Copy link

Thanks for this, it was very handy. You can eliminate the StartCheckingLog and DoneCheckingLog if you just set AutoReset = false and then add _t.Start() in place of DoneCheckingLog(). Accomplishes the same task but with less code and confusion. I also added if (_currentSize > newSize) _currentSize = 0, this will allow for rolling log files where a new log file is started.

@cheolsoon
Copy link

thank you!!!!

@kevandrada
Copy link

I have tried this but my textbox or listbox only gets updated after the whole execution has been processed not with every changed(new line added) on my logfile. It's not realtime updating on my end whenever my logfile has a new line of string.

BTW, im doing from logfile to textbox/listbox and it must be realtime changing on the textbox if there's change on the logfile.

If anyone can help. thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment