Created
May 27, 2015 09:40
-
-
Save mikedavies-dev/989dd86a1ace38a9ac58 to your computer and use it in GitHub Desktop.
A simple log file monitor class for .NET
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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(); | |
} | |
} | |
} | |
thank you!!!!
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
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.