From 126b9aa2039cb277cfbaeddcdce98d70b8ef09e2 Mon Sep 17 00:00:00 2001 From: EonaCat Date: Thu, 12 Feb 2026 19:02:33 +0100 Subject: [PATCH] Updated --- .../EonaCat.Logger.LogClient.csproj | 2 +- EonaCat.Logger/EonaCat.Logger.csproj | 12 +- .../EonaCatCoreLogger/FileLoggerProvider.cs | 110 ++++++++++++++---- 3 files changed, 97 insertions(+), 27 deletions(-) diff --git a/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj b/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj index edc9919..8eb2711 100644 --- a/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj +++ b/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj @@ -25,7 +25,7 @@ - + diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj index af69bce..e2a2947 100644 --- a/EonaCat.Logger/EonaCat.Logger.csproj +++ b/EonaCat.Logger/EonaCat.Logger.csproj @@ -13,8 +13,8 @@ EonaCat (Jeroen Saey) EonaCat;Logger;EonaCatLogger;Log;Writer;Jeroen;Saey - 1.6.9 - 1.6.9 + 1.7.0 + 1.7.0 README.md True LICENSE @@ -70,11 +70,11 @@ - - - + + + - + diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs index 98da27d..87f2db7 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs @@ -4,6 +4,7 @@ using EonaCat.Logger.Managers; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; +using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -13,13 +14,14 @@ using System.Threading; using System.Threading.Tasks; [ProviderAlias("EonaCatFileLogger")] -public sealed class FileLoggerProvider : BatchingLoggerProvider +public sealed class FileLoggerProvider : BatchingLoggerProvider, IDisposable { private readonly string _path; private readonly string _fileNamePrefix; private readonly int _maxFileSize; private readonly int _maxRetainedFiles; private readonly int _maxRolloverFiles; + private bool _disposed; private readonly LoggerScopedContext _context = new LoggerScopedContext(); private readonly ConcurrentDictionary _files = new(); @@ -39,17 +41,45 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider private readonly Timer _flushTimer; private readonly TimeSpan _flushInterval = TimeSpan.FromMilliseconds(500); - private sealed class FileState + private sealed class FileState : IDisposable { public string FilePath; public long Size; public DateTime Date; - public byte[] Buffer = new byte[BufferSize]; + public byte[] Buffer = ArrayPool.Shared.Rent(BufferSize); public int BufferPosition; public FileStream Stream; - public SemaphoreSlim WriteLock = new(1, 1); // async safe + public SemaphoreSlim WriteLock = new(1, 1); + + public void Dispose() + { + try + { + if (Buffer != null) + { + ArrayPool.Shared.Return(Buffer); + Buffer = null; + } + + Stream?.Dispose(); + } + catch + { + // Do nothing + } + + try + { + WriteLock?.Dispose(); + } + catch + { + // Do nothing + } + } } + public FileLoggerProvider(IOptions options) : base(options) { var o = options.Value ?? throw new ArgumentNullException(nameof(options)); @@ -68,7 +98,42 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider _files[string.Empty] = defaultState; // Periodic flush - _flushTimer = new Timer(_ => PeriodicFlushAsync().ConfigureAwait(false), null, _flushInterval, _flushInterval); + _flushTimer = new Timer(FlushTimerCallback, null, _flushInterval, _flushInterval); + } + + private void FlushTimerCallback(object state) + { + try + { + PeriodicFlushAsync().GetAwaiter().GetResult(); + } + catch + { + // swallow - avoid timer thread crash + } + } + + private void CleanupUnusedCategories() + { + var now = DateTime.UtcNow; + + foreach (var kv in _files.ToArray()) + { + var state = kv.Value; + + // Remove file states older than 2 days and empty queues + if ((now - state.Date).TotalDays > 2 && + _messageQueues.TryGetValue(kv.Key, out var queue) && + queue.IsEmpty) + { + if (_files.TryRemove(kv.Key, out var removed)) + { + removed.Dispose(); + } + + _messageQueues.TryRemove(kv.Key, out _); + } + } } internal override Task WriteMessagesAsync(IReadOnlyList messages, CancellationToken token) @@ -118,22 +183,15 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider _files[key] = state; } - var messagesToWrite = new List(); - while (queue.TryDequeue(out var msg)) - { - messagesToWrite.Add(msg); - } - - if (messagesToWrite.Count > 0) + if (!queue.IsEmpty) { + await state.WriteLock.WaitAsync(); try { - await state.WriteLock.WaitAsync(); - WriteBatch(state, messagesToWrite, key); - } - catch (Exception ex) - { - OnError?.Invoke(this, new ErrorMessage { Exception = ex, Message = ex.Message }); + while (queue.TryDequeue(out var msg)) + { + WriteBatch(state, new[] { msg }, key); + } } finally { @@ -143,6 +201,7 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider } DeleteOldLogFiles(); + CleanupUnusedCategories(); } private void WriteBatch(FileState state, IEnumerable messages, string categoryKey) @@ -376,22 +435,33 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider protected override void OnShutdownFlush() { _flushTimer?.Dispose(); - PeriodicFlushAsync().GetAwaiter().GetResult(); + + try + { + PeriodicFlushAsync().GetAwaiter().GetResult(); + } + catch + { + // Do nothing + } foreach (var state in _files.Values) { try { FlushBufferAsync(state).GetAwaiter().GetResult(); - state.Stream?.Dispose(); } catch { // Do nothing } + + state.Dispose(); } _files.Clear(); _messageQueues.Clear(); + + base.OnShutdownFlush(); } }