This commit is contained in:
2026-02-12 19:02:33 +01:00
parent 8257b7d9ce
commit 126b9aa203
3 changed files with 97 additions and 27 deletions

View File

@@ -25,7 +25,7 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Net.Http.Json" Version="10.0.1" />
<PackageReference Include="System.Net.Http.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EonaCat.Logger\EonaCat.Logger.csproj" />

View File

@@ -13,8 +13,8 @@
<Copyright>EonaCat (Jeroen Saey)</Copyright>
<PackageTags>EonaCat;Logger;EonaCatLogger;Log;Writer;Jeroen;Saey</PackageTags>
<PackageIconUrl />
<Version>1.6.9</Version>
<FileVersion>1.6.9</FileVersion>
<Version>1.7.0</Version>
<FileVersion>1.7.0</FileVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
@@ -70,11 +70,11 @@
</PackageReference>
<PackageReference Include="EonaCat.Versioning.Helpers" Version="1.0.2" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Threading.Channels" Version="10.0.1" />
<PackageReference Include="System.Threading.Channels" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<None Update="LICENSE.md">

View File

@@ -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<string, FileState> _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<byte>.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<byte>.Shared.Return(Buffer);
Buffer = null;
}
Stream?.Dispose();
}
catch
{
// Do nothing
}
try
{
WriteLock?.Dispose();
}
catch
{
// Do nothing
}
}
}
public FileLoggerProvider(IOptions<FileLoggerOptions> 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<LogMessage> messages, CancellationToken token)
@@ -118,22 +183,15 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
_files[key] = state;
}
var messagesToWrite = new List<LogMessage>();
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<LogMessage> 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();
}
}