Updated
This commit is contained in:
@@ -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" />
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user