0

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;
            }
        }
    }
Andrew Burns
  • 346
  • 4
  • 15
  • The [FileSystemWatcher class](https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx) is not an option? – Steve Aug 27 '16 at 07:45
  • to be honest, i haven't tried this option, but I have read around that this FileSystemWatcher is not always accurate. – Andrew Burns Aug 27 '16 at 07:46
  • With only 4 files to monitor you shouldn't have any kind of problems http://stackoverflow.com/questions/4855646/reliable-way-of-monitoring-file-changes-in-a-directory-using-the-net-framework – Steve Aug 27 '16 at 07:51
  • ive noticed an issue with this already: http://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice. not a good start – Andrew Burns Aug 27 '16 at 08:05
  • A workaround to something that should just work doesn't give me any trust – Andrew Burns Aug 27 '16 at 08:14

0 Answers0