diff --git a/EonaCat.LogStack/EonaCat.LogStack.csproj b/EonaCat.LogStack/EonaCat.LogStack.csproj index 7ccef21..d4a1243 100644 --- a/EonaCat.LogStack/EonaCat.LogStack.csproj +++ b/EonaCat.LogStack/EonaCat.LogStack.csproj @@ -14,7 +14,7 @@ It features a rich fluent API for routing log events to dozens of destinations f EonaCat (Jeroen Saey) EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey - 0.1.9 + 0.2.0 README.md True LICENSE @@ -25,7 +25,7 @@ It features a rich fluent API for routing log events to dozens of destinations f - 0.1.9+{chash:10}.{c:ymd} + 0.2.0+{chash:10}.{c:ymd} true true v[0-9]* @@ -36,7 +36,7 @@ It features a rich fluent API for routing log events to dozens of destinations f - 0.1.9 + 0.2.0 EonaCat.LogStack EonaCat.LogStack https://git.saey.me/EonaCat/EonaCat.LogStack diff --git a/EonaCat.LogStack/EonaCatLogger.cs b/EonaCat.LogStack/EonaCatLogger.cs index 0f4139d..ce918dd 100644 --- a/EonaCat.LogStack/EonaCatLogger.cs +++ b/EonaCat.LogStack/EonaCatLogger.cs @@ -718,54 +718,93 @@ namespace EonaCat.LogStack // Drain and stop the async channel pipeline if active if (_asyncChannel != null) { - // Flush all logs from flows first to ensure they're written to disk - await FlushAsync().ConfigureAwait(false); - - // Complete the channel - no more writes allowed + // Complete the channel immediately - no more writes allowed _asyncChannel.Writer.TryComplete(); if (_asyncConsumer != null) { try { - // Wait for the consumer task to finish draining the channel - // Use a generous timeout (30 seconds) since we're dealing with legitimate I/O - // but prevent infinite hangs if something is truly deadlocked - using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30))) + // Give the consumer a very short window (1 second) to finish naturally + // If it doesn't complete in time, we proceed anyway to not block app shutdown + // This is critical for ensuring the logger never blocks application exit + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1))) { try { await _asyncConsumer.ConfigureAwait(false); } catch (OperationCanceledException) - { - // Do nothing + { + // Timeout expired - consumer didn't finish in time + // This is acceptable; the background task will continue draining + // but we don't wait for it to allow app exit } } } catch (Exception ex) { - // Do nothing + // Ignore any errors during consumer shutdown + } + } + + // Attempt to flush flows with a very short timeout + // Don't await indefinitely - prioritize app exit over log flushing + try + { + using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500))) + { + await FlushAsync(cts.Token).ConfigureAwait(false); + } + } + catch (OperationCanceledException) + { + // Flush timed out - that's OK, some logs might be lost but app exits cleanly + } + catch (Exception ex) + { + // Ignore flush errors + } + + // Dispose flows with a short timeout + var disposeTasks = _concurrentFlows.Select(f => f.DisposeAsync().AsTask()).ToArray(); + if (disposeTasks.Length > 0) + { + try + { + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1))) + { + await Task.WhenAll(disposeTasks).ConfigureAwait(false); + } + } + catch (OperationCanceledException) + { + // Disposal timed out - proceed anyway + } + catch (Exception ex) + { + // Ignore disposal errors } } } else { - // If using synchronous mode, just flush - await FlushAsync().ConfigureAwait(false); - } - - // Dispose flows - they should already be flushed above - var disposeTasks = _concurrentFlows.Select(f => f.DisposeAsync().AsTask()).ToArray(); - try - { - await Task.WhenAll(disposeTasks).ConfigureAwait(false); - } - catch (Exception ex) - { - // Do nothing + // If using synchronous mode, attempt a quick flush but don't block + try + { + using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500))) + { + await FlushAsync(cts.Token).ConfigureAwait(false); + } + } + catch + { + // Ignore errors during sync-mode flush + } } + // Request cancellation of the async pipeline if it's still running + _asyncCts?.Cancel(); _asyncCts?.Dispose(); GC.SuppressFinalize(this); } diff --git a/EonaCat.LogStack/EonaCatLoggerCore/Flows/FileFlow.cs b/EonaCat.LogStack/EonaCatLoggerCore/Flows/FileFlow.cs index 19fa2c9..a1c07e5 100644 --- a/EonaCat.LogStack/EonaCatLoggerCore/Flows/FileFlow.cs +++ b/EonaCat.LogStack/EonaCatLoggerCore/Flows/FileFlow.cs @@ -223,7 +223,7 @@ namespace EonaCat.LogStack.Flows // Dedicated writer thread _writerThread = new Thread(WriterThreadBody) { - IsBackground = false, + IsBackground = true, Name = "FileFlow.Writer[" + filePrefix + "]", Priority = ThreadPriority.AboveNormal, };