From c50c22e8148dd6d1dd0a6ec9c4131256bc4a4688 Mon Sep 17 00:00:00 2001 From: Randall Flagg Date: Thu, 12 Jun 2025 23:53:05 +0300 Subject: [PATCH] Removed the thread that watches file changes. Handle local file and file:// URI. SFTP not working. --- .../Classes/Log/LogfileReader.cs | 265 +++++++++++------- src/LogExpert.Core/LogExpert.Core.csproj | 1 + 2 files changed, 161 insertions(+), 105 deletions(-) diff --git a/src/LogExpert.Core/Classes/Log/LogfileReader.cs b/src/LogExpert.Core/Classes/Log/LogfileReader.cs index 279b9668..3031cfcb 100644 --- a/src/LogExpert.Core/Classes/Log/LogfileReader.cs +++ b/src/LogExpert.Core/Classes/Log/LogfileReader.cs @@ -3,7 +3,6 @@ using LogExpert.Core.Classes.xml; using LogExpert.Core.Entities; using LogExpert.Core.EventArguments; -using LogExpert.Core.EventHandlers; using LogExpert.Core.Interface; using NLog; @@ -18,7 +17,17 @@ public class LogfileReader : IAutoLogLineColumnizerCallback private readonly GetLogLineFx _logLineFx; - private readonly string _fileName; + private string FileName + { + get; init + { + var uri = new Uri(value); + if (uri.IsFile) + { + field = uri.LocalPath; // Convert the URI to a local file path + } + } + } private readonly int _max_buffers; private readonly int _maxLinesPerBuffer; @@ -35,7 +44,6 @@ public class LogfileReader : IAutoLogLineColumnizerCallback private long _fileLength; private Task _garbageCollectorTask; - private Task _monitorTask; private readonly CancellationTokenSource cts = new(); private bool _isDeleted; @@ -47,6 +55,7 @@ public class LogfileReader : IAutoLogLineColumnizerCallback private ReaderWriterLock _lruCacheDictLock; + private FileSystemWatcher _watcher; private bool _shouldStop; private ILogFileInfo _watchedILogFileInfo; @@ -56,12 +65,15 @@ public class LogfileReader : IAutoLogLineColumnizerCallback public LogfileReader (string fileName, EncodingOptions encodingOptions, bool multiFile, int bufferCount, int linesPerBuffer, MultiFileOptions multiFileOptions, IPluginRegistry pluginRegistry) { + PreProcessColumnizer = null; + IsXmlMode = false; +//TODO: This is not a good approach. The constructor should be called only if it has valid options and throw an exception if not. this is a bad mix. if (fileName == null) { return; } - _fileName = fileName; + FileName = fileName; EncodingOptions = encodingOptions; IsMultiFile = multiFile; _max_buffers = bufferCount; @@ -95,6 +107,9 @@ public LogfileReader (string fileName, EncodingOptions encodingOptions, bool mul public LogfileReader (string[] fileNames, EncodingOptions encodingOptions, int bufferCount, int linesPerBuffer, MultiFileOptions multiFileOptions, IPluginRegistry pluginRegistry) { + PreProcessColumnizer = null; + IsXmlMode = false; +//TODO: This is not a good approach. The constructor should be called only if it has valid options and throw an exception if not. this is a bad mix. if (fileNames == null || fileNames.Length < 1) { return; @@ -117,7 +132,7 @@ public LogfileReader (string[] fileNames, EncodingOptions encodingOptions, int b } _watchedILogFileInfo = fileInfo; - _fileName = fileInfo.FullName; + FileName = fileInfo.FullName; StartGCThread(); } @@ -125,24 +140,18 @@ public LogfileReader (string[] fileNames, EncodingOptions encodingOptions, int b #endregion #region Delegates - - public delegate void BlockLoadedEventHandler (object sender, LoadFileEventArgs e); - public delegate void FileNotFoundEventHandler (object sender, EventArgs e); - public delegate void FileRespawnedEventHandler (object sender, EventArgs e); - public delegate void FinishedLoadingEventHandler (object sender, EventArgs e); private delegate Task GetLogLineFx (int lineNum); - public delegate void LoadingStartedEventHandler (object sender, LoadFileEventArgs e); #endregion #region Events - public event FileSizeChangedEventHandler FileSizeChanged; - public event BlockLoadedEventHandler LoadFile; - public event LoadingStartedEventHandler LoadingStarted; - public event FinishedLoadingEventHandler LoadingFinished; - public event FileNotFoundEventHandler FileNotFound; - public event FileRespawnedEventHandler Respawned; + public event EventHandler FileSizeChanged; + public event EventHandler LoadFile; + public event EventHandler LoadingStarted; + public event EventHandler LoadingFinished; + public event EventHandler FileNotFound; + public event EventHandler Respawned; #endregion @@ -180,7 +189,7 @@ public int LineCount public IXmlLogConfiguration XmlLogConfig { get; set; } - public IPreProcessColumnizer PreProcessColumnizer { get; set; } + public IPreProcessColumnizer PreProcessColumnizer { get; set; } = null; public EncodingOptions EncodingOptions { @@ -258,7 +267,7 @@ public void ReadFiles () /// public int ShiftBuffers () { - _logger.Info("ShiftBuffers() begin for {0}{1}", _fileName, IsMultiFile ? " (MultiFile)" : ""); + _logger.Info("ShiftBuffers() begin for {0}{1}", FileName, IsMultiFile ? " (MultiFile)" : ""); AcquireBufferListWriterLock(); var offset = 0; _isLineCountDirty = true; @@ -561,28 +570,142 @@ public int GetRealLineNumForVirtualLineNum (int lineNum) return result; } - public void StartMonitoring () + public async Task StartMonitoring () { - _logger.Info("startMonitoring()"); - _monitorTask = Task.Run(MonitorThreadProc, cts.Token); + _logger.Info("StartMonitoring() for file ${_watchedILogFileInfo.FullName}"); + + await Task.Run(() => + { + _logger.Info("MonitorThreadProc() for file {0}", _watchedILogFileInfo.FullName); + + long oldSize = 0; + try + { + OnLoadingStarted(new LoadFileEventArgs(FileName, 0, false, 0, false)); + ReadFiles(); + if (!_isDeleted) + { + oldSize = _fileLength; + OnLoadingFinished(); + } + } + catch (Exception e) + { + _logger.Error(e); + } + }); + + try + { + _watcher = new FileSystemWatcher + { + NotifyFilter = //NotifyFilters.Attributes + //| NotifyFilters.CreationTime + //| NotifyFilters.DirectoryName + //| + NotifyFilters.FileName + //| NotifyFilters.LastAccess + | NotifyFilters.LastWrite + //| NotifyFilters.Security + | NotifyFilters.Size, + + Path = Path.GetDirectoryName(FileName) ?? throw new ArgumentException("Invalid file path"), + Filter = Path.GetFileName(FileName), // Sets filter to the specific + EnableRaisingEvents = true + }; + } + catch (UnauthorizedAccessException ex) + { + Console.WriteLine($"Access denied: {ex.Message}"); + } + catch (ArgumentException ex) + { + Console.WriteLine($"Invalid argument: {ex.Message}"); + } + catch (Exception ex) + { + Console.WriteLine($"An error occurred: {ex.Message}"); + } + + _watcher.Error += (sender, e) => + { + Console.WriteLine($"Error occurred: {e.GetException().Message}"); + Task.Delay(5000).ContinueWith(_ => + { + try + { + Console.WriteLine("Attempting to restart the watcher..."); + _watcher.EnableRaisingEvents = true; + } + catch (Exception ex) + { + Console.WriteLine($"Failed to restart the watcher: {ex.Message}"); + } + }); + }; + _watcher.Changed += OnFileChanged; + _watcher.Created += OnCreated; + _watcher.Deleted += OnFileDeleted; + _watcher.Renamed += OnFileRenamed; + _watcher.Error += OnFileError; + _shouldStop = false; } + private void OnFileError (object sender, ErrorEventArgs e) + { + throw new NotImplementedException(); + } + + private void OnFileRenamed (object sender, RenamedEventArgs e) + { + throw new NotImplementedException(); + } + + private void OnCreated (object sender, FileSystemEventArgs e) + { + //TODO: This should be deleted before merge? + throw new NotImplementedException(); + } + + private void OnFileDeleted (object sender, FileSystemEventArgs e) + { + MonitoredFileNotFound(); + } + + private void OnFileChanged (object sender, FileSystemEventArgs e) + { + try + { + _watchedILogFileInfo.FileHasChanged(); + _fileLength = _watchedILogFileInfo.Length; + FileChanged(); + } + catch (FileNotFoundException ex) + { + MonitoredFileNotFound(); + } + catch (Exception ex) + { + throw new NotImplementedException(); + } + } + public void StopMonitoring () { _logger.Info("stopMonitoring()"); _shouldStop = true; - Thread.Sleep(_watchedILogFileInfo.PollInterval); // leave time for the threads to stop by themselves - - if (_monitorTask != null) + if (_watcher != null) { - if (_monitorTask.Status == TaskStatus.Running) // if thread has not finished, abort it - { - cts.Cancel(); - } + _watcher.EnableRaisingEvents = false; // Stop watching + _watcher.Dispose(); // Release resources + _watcher = null; // Clear the reference + } + Thread.Sleep(_watchedILogFileInfo.PollInterval); // leave time for the threads to stop by themselves + if (_garbageCollectorTask.IsCanceled == false) { if (_garbageCollectorTask.Status == TaskStatus.Running) // if thread has not finished, abort it @@ -620,11 +743,11 @@ public void DeleteAllContent () { if (_contentDeleted) { - _logger.Debug("Buffers for {0} already deleted.", Util.GetNameFromPath(_fileName)); + _logger.Debug("Buffers for {0} already deleted.", Util.GetNameFromPath(FileName)); return; } - _logger.Info("Deleting all log buffers for {0}. Used mem: {1:N0}", Util.GetNameFromPath(_fileName), GC.GetTotalMemory(true)); //TODO [Z] uh GC collect calls creepy + _logger.Info("Deleting all log buffers for {0}. Used mem: {1:N0}", Util.GetNameFromPath(FileName), GC.GetTotalMemory(true)); //TODO [Z] uh GC collect calls creepy AcquireBufferListWriterLock(); _lruCacheDictLock.AcquireWriterLock(Timeout.Infinite); _disposeLock.AcquireWriterLock(Timeout.Infinite); @@ -691,7 +814,7 @@ public void LogBufferInfoForLine (int lineNum) if (buffer == null) { ReleaseBufferListReaderLock(); - _logger.Error("Cannot find buffer for line {0}, file: {1}{2}", lineNum, _fileName, IsMultiFile ? " (MultiFile)" : ""); + _logger.Error("Cannot find buffer for line {0}, file: {1}{2}", lineNum, FileName, IsMultiFile ? " (MultiFile)" : ""); return; } @@ -716,7 +839,7 @@ public void LogBufferDiagnostic () _lruCacheDictLock.ReleaseReaderLock(); AcquireBufferListReaderLock(); - _logger.Info("File: {0}\r\nBuffer count: {1}\r\nDisposed buffers: {2}", _fileName, _bufferList.Count, _bufferList.Count - cacheCount); + _logger.Info("File: {0}\r\nBuffer count: {1}\r\nDisposed buffers: {2}", FileName, _bufferList.Count, _bufferList.Count - cacheCount); var lineNum = 0; long disposeSum = 0; long maxDispose = 0; @@ -772,7 +895,7 @@ private Task GetLogLineInternal (int lineNum) if (logBuffer == null) { ReleaseBufferListReaderLock(); - _logger.Error("Cannot find buffer for line {0}, file: {1}{2}", lineNum, _fileName, IsMultiFile ? " (MultiFile)" : ""); + _logger.Error("Cannot find buffer for line {0}, file: {1}{2}", lineNum, FileName, IsMultiFile ? " (MultiFile)" : ""); return null; } @@ -1142,7 +1265,7 @@ private void GarbageCollectLruCache () #if DEBUG if (diff > 0) { - _logger.Info("Removing {0} entries from LRU cache for {1}", diff, Util.GetNameFromPath(_fileName)); + _logger.Info("Removing {0} entries from LRU cache for {1}", diff, Util.GetNameFromPath(FileName)); } #endif SortedList useSorterList = []; @@ -1459,74 +1582,6 @@ private LogBuffer GetFirstBufferForFileByLogBuffer (LogBuffer logBuffer) return resultBuffer; } - private void MonitorThreadProc () - { - Thread.CurrentThread.Name = "MonitorThread"; - //IFileSystemPlugin fs = PluginRegistry.GetInstance().FindFileSystemForUri(this.watchedILogFileInfo.FullName); - _logger.Info("MonitorThreadProc() for file {0}", _watchedILogFileInfo.FullName); - - long oldSize; - try - { - OnLoadingStarted(new LoadFileEventArgs(_fileName, 0, false, 0, false)); - ReadFiles(); - if (!_isDeleted) - { - oldSize = _fileLength; - OnLoadingFinished(); - } - } - catch (Exception e) - { - _logger.Error(e); - } - - while (!_shouldStop) - { - try - { - var pollInterval = _watchedILogFileInfo.PollInterval; - //#if DEBUG - // if (_logger.IsDebug) - // { - // _logger.logDebug("Poll interval for " + this.fileName + ": " + pollInterval); - // } - //#endif - Thread.Sleep(pollInterval); - } - catch (Exception e) - { - _logger.Error(e); - } - - if (_shouldStop) - { - return; - } - - try - { - if (_watchedILogFileInfo.FileHasChanged()) - { - _fileLength = _watchedILogFileInfo.Length; - if (_fileLength == -1) - { - MonitoredFileNotFound(); - } - else - { - oldSize = _fileLength; - FileChanged(); - } - } - } - catch (FileNotFoundException) - { - MonitoredFileNotFound(); - } - } - } - private void MonitoredFileNotFound () { long oldSize; @@ -1558,7 +1613,7 @@ private void FileChanged () var newSize = _fileLength; //if (this.currFileSize != newSize) { - _logger.Info("file size changed. new size={0}, file: {1}", newSize, _fileName); + _logger.Info("file size changed. new size={0}, file: {1}", newSize, FileName); FireChangeEvent(); } } @@ -1581,7 +1636,7 @@ private void FireChangeEvent () { // ReloadBufferList(); // removed because reloading is triggered by owning LogWindow // Trigger "new file" handling (reload) - OnLoadFile(new LoadFileEventArgs(_fileName, 0, true, _fileLength, true)); + OnLoadFile(new LoadFileEventArgs(FileName, 0, true, _fileLength, true)); if (_isDeleted) { diff --git a/src/LogExpert.Core/LogExpert.Core.csproj b/src/LogExpert.Core/LogExpert.Core.csproj index c31d226e..61fa96f4 100644 --- a/src/LogExpert.Core/LogExpert.Core.csproj +++ b/src/LogExpert.Core/LogExpert.Core.csproj @@ -9,6 +9,7 @@ True ..\Solution Items\Key.snk false + preview