I have set up a threaded timer that will fire every x seconds (in this case every 5 seconds) checks a directory (in this case all .log files (4 in total) if it picks up a log file has changed it will take the new lines and send them off to be logged to Log Stash. This app will be running as a Windows service on a Windows Server 2012 box.
this is working as it stands, however I want to make sure that i am not in danger of letting me threads get out of hand and cause massive cpu issues on the server.
I am not interesting in the solution that i have to check if a file has changed, what I am interested in is peoples opinion on the threads and and any improvements/safe guards i need to put in place to prevent cpu leakage.
here is my code:
protected override void OnStart(string[] args)
{
var statusChecker = new LogFileMonitor(@"C:\DEV\LOGS\", "\r\n");
// _timer = new Timer(statusChecker.CheckStatus,
// null, 1000, 30000);
//_timer = new Timer(statusChecker.CheckStatus, null, 5000, Timeout.Infinite);
_timer = new Timer((g) =>
{
statusChecker.CheckStatus();
_timer.Change(5000, Timeout.Infinite);
}, null, (long) 0, Timeout.Infinite);
}
public class LogFileMonitor
{
private string Delimiter = String.Empty;
private List<LogFileInfo> LogFileInfo;
private string _buffer = "";
private readonly LogStashHelper _logStashHelper;
private volatile bool _executing = false;
public LogFileMonitor(string path, string delimiter = "\n")
{
var logList = new List<LogFileInfo>();
var logStashHelper = new LogStashHelper();
string[] files = Directory.GetFiles(path, "*.log", SearchOption.AllDirectories);
foreach (var fileName in files)
{
var createDate = new FileInfo(fileName).CreationTime;
var size = new FileInfo(fileName).Length;
logList.Add(new LogFileInfo(fileName, size, createDate));
}
_logStashHelper = logStashHelper;
LogFileInfo = logList;
Delimiter = delimiter;
}
// This method is called by the timer delegate.
public void CheckStatus()
{
if (_executing)
return;
_executing = true;
var index = 0;
try
{
foreach (var fileInfo in LogFileInfo)
{
var fileName = fileInfo.FileName;
var originalSize = fileInfo.FileSize;
var currentCreateDate = fileInfo.FileCreateDate;
Console.WriteLine("checking file: " + fileName);
// get the new size
var newSize = new FileInfo(fileInfo.FileName).Length;
//get the new date creation
var newCreateDate = new FileInfo(fileInfo.FileName).CreationTime;
if (currentCreateDate < newCreateDate)
{
//then we have a new file reset defai
_buffer = String.Empty;
LogFileInfo[index].FileSize = new FileInfo(fileInfo.FileName).Length;
LogFileInfo[index].FileCreateDate = new FileInfo(fileInfo.FileName).CreationTime;
newSize = new FileInfo(fileInfo.FileName).Length;
originalSize = 0;
}
if (originalSize >= newSize)
{
//do nothing
}
else
{
// read the contents of the file
using (
var stream = File.Open(fileName, FileMode.Open, FileAccess.Read,
FileShare.ReadWrite))
using (var sr = new StreamReader(stream))
{
// seek to the current file position
sr.BaseStream.Seek(originalSize, 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, System.StringComparison.Ordinal) == -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, System.StringComparison.Ordinal) +
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);
//List<string> text = File.ReadLines("file.txt").Reverse().Take(2).ToList();
// send back to caller, NOTE: this is done from a different thread!
foreach (var line in lines)
{
_logStashHelper.SendToLogStash(line, MessageCategory.Information,
new Dictionary<object, object>(), fileName);
}
stream.Close();
}
// set the new current position
LogFileInfo[index].FileSize = newSize;
}
index++;
}
}
catch (Exception e)
{
}
finally
{
_executing = false;
}
}
}