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> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Net.Http.Json" Version="10.0.1" /> <PackageReference Include="System.Net.Http.Json" Version="10.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\EonaCat.Logger\EonaCat.Logger.csproj" /> <ProjectReference Include="..\EonaCat.Logger\EonaCat.Logger.csproj" />

View File

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

View File

@@ -4,6 +4,7 @@ using EonaCat.Logger.Managers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using System;
using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -13,13 +14,14 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
[ProviderAlias("EonaCatFileLogger")] [ProviderAlias("EonaCatFileLogger")]
public sealed class FileLoggerProvider : BatchingLoggerProvider public sealed class FileLoggerProvider : BatchingLoggerProvider, IDisposable
{ {
private readonly string _path; private readonly string _path;
private readonly string _fileNamePrefix; private readonly string _fileNamePrefix;
private readonly int _maxFileSize; private readonly int _maxFileSize;
private readonly int _maxRetainedFiles; private readonly int _maxRetainedFiles;
private readonly int _maxRolloverFiles; private readonly int _maxRolloverFiles;
private bool _disposed;
private readonly LoggerScopedContext _context = new LoggerScopedContext(); private readonly LoggerScopedContext _context = new LoggerScopedContext();
private readonly ConcurrentDictionary<string, FileState> _files = new(); private readonly ConcurrentDictionary<string, FileState> _files = new();
@@ -39,17 +41,45 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
private readonly Timer _flushTimer; private readonly Timer _flushTimer;
private readonly TimeSpan _flushInterval = TimeSpan.FromMilliseconds(500); private readonly TimeSpan _flushInterval = TimeSpan.FromMilliseconds(500);
private sealed class FileState private sealed class FileState : IDisposable
{ {
public string FilePath; public string FilePath;
public long Size; public long Size;
public DateTime Date; public DateTime Date;
public byte[] Buffer = new byte[BufferSize]; public byte[] Buffer = ArrayPool<byte>.Shared.Rent(BufferSize);
public int BufferPosition; public int BufferPosition;
public FileStream Stream; 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) public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
{ {
var o = options.Value ?? throw new ArgumentNullException(nameof(options)); var o = options.Value ?? throw new ArgumentNullException(nameof(options));
@@ -68,7 +98,42 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
_files[string.Empty] = defaultState; _files[string.Empty] = defaultState;
// Periodic flush // 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) internal override Task WriteMessagesAsync(IReadOnlyList<LogMessage> messages, CancellationToken token)
@@ -118,22 +183,15 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
_files[key] = state; _files[key] = state;
} }
var messagesToWrite = new List<LogMessage>(); if (!queue.IsEmpty)
while (queue.TryDequeue(out var msg))
{
messagesToWrite.Add(msg);
}
if (messagesToWrite.Count > 0)
{
try
{ {
await state.WriteLock.WaitAsync(); await state.WriteLock.WaitAsync();
WriteBatch(state, messagesToWrite, key); try
}
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 finally
{ {
@@ -143,6 +201,7 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
} }
DeleteOldLogFiles(); DeleteOldLogFiles();
CleanupUnusedCategories();
} }
private void WriteBatch(FileState state, IEnumerable<LogMessage> messages, string categoryKey) private void WriteBatch(FileState state, IEnumerable<LogMessage> messages, string categoryKey)
@@ -376,22 +435,33 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
protected override void OnShutdownFlush() protected override void OnShutdownFlush()
{ {
_flushTimer?.Dispose(); _flushTimer?.Dispose();
try
{
PeriodicFlushAsync().GetAwaiter().GetResult(); PeriodicFlushAsync().GetAwaiter().GetResult();
}
catch
{
// Do nothing
}
foreach (var state in _files.Values) foreach (var state in _files.Values)
{ {
try try
{ {
FlushBufferAsync(state).GetAwaiter().GetResult(); FlushBufferAsync(state).GetAwaiter().GetResult();
state.Stream?.Dispose();
} }
catch catch
{ {
// Do nothing // Do nothing
} }
state.Dispose();
} }
_files.Clear(); _files.Clear();
_messageQueues.Clear(); _messageQueues.Clear();
base.OnShutdownFlush();
} }
} }