This commit is contained in:
2026-08-04 15:57:32 +02:00
committed by Jeroen Saey
parent 82fac06d8f
commit 3e22c31460
3 changed files with 67 additions and 28 deletions
+3 -3
View File
@@ -14,7 +14,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
<Copyright>EonaCat (Jeroen Saey)</Copyright> <Copyright>EonaCat (Jeroen Saey)</Copyright>
<PackageTags>EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey</PackageTags> <PackageTags>EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey</PackageTags>
<PackageIconUrl /> <PackageIconUrl />
<FileVersion>0.1.9</FileVersion> <FileVersion>0.2.0</FileVersion>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<GenerateDocumentationFile>True</GenerateDocumentationFile> <GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
@@ -25,7 +25,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<EVRevisionFormat>0.1.9+{chash:10}.{c:ymd}</EVRevisionFormat> <EVRevisionFormat>0.2.0+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVDefault>true</EVDefault> <EVDefault>true</EVDefault>
<EVInfo>true</EVInfo> <EVInfo>true</EVInfo>
<EVTagMatch>v[0-9]*</EVTagMatch> <EVTagMatch>v[0-9]*</EVTagMatch>
@@ -36,7 +36,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Version>0.1.9</Version> <Version>0.2.0</Version>
<PackageId>EonaCat.LogStack</PackageId> <PackageId>EonaCat.LogStack</PackageId>
<Product>EonaCat.LogStack</Product> <Product>EonaCat.LogStack</Product>
<RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack</RepositoryUrl> <RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack</RepositoryUrl>
+59 -20
View File
@@ -718,20 +718,17 @@ namespace EonaCat.LogStack
// Drain and stop the async channel pipeline if active // Drain and stop the async channel pipeline if active
if (_asyncChannel != null) if (_asyncChannel != null)
{ {
// Flush all logs from flows first to ensure they're written to disk // Complete the channel immediately - no more writes allowed
await FlushAsync().ConfigureAwait(false);
// Complete the channel - no more writes allowed
_asyncChannel.Writer.TryComplete(); _asyncChannel.Writer.TryComplete();
if (_asyncConsumer != null) if (_asyncConsumer != null)
{ {
try try
{ {
// Wait for the consumer task to finish draining the channel // Give the consumer a very short window (1 second) to finish naturally
// Use a generous timeout (30 seconds) since we're dealing with legitimate I/O // If it doesn't complete in time, we proceed anyway to not block app shutdown
// but prevent infinite hangs if something is truly deadlocked // This is critical for ensuring the logger never blocks application exit
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30))) using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
{ {
try try
{ {
@@ -739,33 +736,75 @@ namespace EonaCat.LogStack
} }
catch (OperationCanceledException) 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) 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 else
{ {
// If using synchronous mode, just flush // If using synchronous mode, attempt a quick flush but don't block
await FlushAsync().ConfigureAwait(false);
}
// Dispose flows - they should already be flushed above
var disposeTasks = _concurrentFlows.Select(f => f.DisposeAsync().AsTask()).ToArray();
try try
{ {
await Task.WhenAll(disposeTasks).ConfigureAwait(false); using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500)))
}
catch (Exception ex)
{ {
// Do nothing 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(); _asyncCts?.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@@ -223,7 +223,7 @@ namespace EonaCat.LogStack.Flows
// Dedicated writer thread // Dedicated writer thread
_writerThread = new Thread(WriterThreadBody) _writerThread = new Thread(WriterThreadBody)
{ {
IsBackground = false, IsBackground = true,
Name = "FileFlow.Writer[" + filePrefix + "]", Name = "FileFlow.Writer[" + filePrefix + "]",
Priority = ThreadPriority.AboveNormal, Priority = ThreadPriority.AboveNormal,
}; };