Filelogger op basis van batch
cfb6cc6dae503c9be193f105e1b109a2e76d042e
1 files changed
LoggerExtensions/FileLogger.cs
diff --git a/LoggerExtensions/FileLogger.cs b/LoggerExtensions/FileLogger.cs
index 8ae0896..0a222e4 100644
--- a/LoggerExtensions/FileLogger.cs
+++ b/LoggerExtensions/FileLogger.cs
@@ -19,8 +19,13 @@ public sealed class FileLogger : ILogger, IDisposable
this.logDirectory = logDirectory;
Directory.CreateDirectory(logDirectory);
- channel = Channel.CreateUnbounded<string>(
- new UnboundedChannelOptions { SingleReader = true, SingleWriter = false });
+ channel = Channel.CreateBounded<string>(
+ new BoundedChannelOptions(10000)
+ {
+ SingleReader = true,
+ SingleWriter = false,
+ FullMode = BoundedChannelFullMode.Wait
+ });
consumerTask = Task.Run(() => ConsumeAsync(cts.Token));
}
@@ -47,21 +52,49 @@ public sealed class FileLogger : ILogger, IDisposable
private async Task ConsumeAsync(CancellationToken ct)
{
+ const int batchSize = 200;
+ const int maxIdleMs = 4000;
+
+ var batch = new List<string>(batchSize);
+
try
{
- await foreach (var message in channel.Reader.ReadAllAsync(ct))
+ while (!ct.IsCancellationRequested)
{
- await WriteMessageWithRetryAsync(message, ct);
+ var readTask = channel.Reader.ReadAsync(ct).AsTask();
+ var delayTask = Task.Delay(maxIdleMs, ct);
+
+ var completed = await Task.WhenAny(readTask, delayTask);
+
+ if (completed == delayTask)
+ {
+ if (batch.Count > 0)
+ {
+ await WriteBatchAsync(batch, ct);
+ batch.Clear();
+ }
+ continue;
+ }
+
+ var message = await readTask;
+ batch.Add(message);
+
+ if (batch.Count >= batchSize)
+ {
+ await WriteBatchAsync(batch, ct);
+ batch.Clear();
+ }
}
}
- catch (OperationCanceledException) { }
- catch (Exception ex)
+ catch (OperationCanceledException)
{
- Console.Error.WriteLine($"FileLogger consumer error: {ex}");
+ if (batch.Count > 0)
+ {
+ try { await WriteBatchAsync(batch, ct); } catch { }
+ }
}
}
-
- private async Task WriteMessageWithRetryAsync(string message, CancellationToken ct)
+ private async Task WriteBatchAsync(List<string> messages, CancellationToken ct)
{
const int maxRetries = 3;
@@ -69,28 +102,22 @@ public sealed class FileLogger : ILogger, IDisposable
{
try
{
- await WriteMessageAsync(message, ct);
+ await WriteBatchInternalAsync(messages, ct);
return;
}
catch (IOException) when (attempt < maxRetries - 1)
{
- await Task.Delay(100 * (int)Math.Pow(2, attempt), ct);
- }
- catch (Exception ex)
- {
- Console.Error.WriteLine($"Failed to write log: {ex.Message}");
- return;
+ await Task.Delay(100 << attempt, ct); // 100, 200, 400 ms
}
}
}
- private async Task WriteMessageAsync(string message, CancellationToken ct)
+ private async Task WriteBatchInternalAsync(List<string> messages, CancellationToken ct)
{
var today = DateTime.Today;
-
if (writer == null || today != currentDate)
{
- await CloseWriterAsync();
+ await CloseWriterAsync(ct);
currentDate = today;
currentFilePath = Path.Combine(logDirectory, $"log-{today:yyyyMMdd}.txt");
@@ -105,20 +132,21 @@ public sealed class FileLogger : ILogger, IDisposable
writer = new StreamWriter(stream) { AutoFlush = false };
}
- if (writer != null)
+ foreach (var msg in messages)
{
- await writer.WriteAsync(message.AsMemory(), ct);
- await writer.FlushAsync(ct);
+ await writer.WriteAsync(msg.AsMemory(), ct);
}
+
+ await writer.FlushAsync(ct);
}
- private async Task CloseWriterAsync()
+ private async Task CloseWriterAsync(CancellationToken ct)
{
if (writer == null) return;
try
{
- await writer.FlushAsync();
+ await writer.FlushAsync(ct);
await writer.DisposeAsync();
}
catch { }
@@ -131,18 +159,19 @@ public sealed class FileLogger : ILogger, IDisposable
public void Dispose()
{
channel.Writer.Complete();
- cts.CancelAfter(TimeSpan.FromSeconds(5));
+ cts.CancelAfter(TimeSpan.FromSeconds(8));
try
{
- consumerTask.Wait(TimeSpan.FromSeconds(6));
+ consumerTask.Wait(TimeSpan.FromSeconds(10));
}
catch { }
- CloseWriterAsync().GetAwaiter().GetResult();
+ CloseWriterAsync(cts.Token).GetAwaiter().GetResult();
cts.Dispose();
}
+
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;