Files
EonaCat.Logger/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs
2026-02-02 20:15:22 +01:00

156 lines
4.3 KiB
C#

using EonaCat.Logger.Managers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
{
private readonly int _batchSize;
private readonly BlockingCollection<LogMessage> _queue;
private readonly CancellationTokenSource _cts = new();
private readonly Task _worker;
private bool _disposed;
private LoggerSettings _loggerSettings;
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
{
var o = options.Value ?? throw new ArgumentNullException(nameof(options));
if (o.FlushPeriod <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(o.FlushPeriod));
}
_batchSize = o.BatchSize > 0 ? o.BatchSize : 100;
_queue = new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>());
if (o is FileLoggerOptions file)
{
UseLocalTime = file.UseLocalTime;
UseMask = file.UseMask;
LoggerSettings = file.LoggerSettings;
}
_worker = Task.Factory.StartNew(
ProcessLoop,
_cts.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
protected bool UseLocalTime { get; }
public bool UseMask { get; }
protected DateTimeOffset NowOffset =>
UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
protected LoggerSettings LoggerSettings
{
get
{
if (_loggerSettings == null)
{
_loggerSettings = new LoggerSettings
{
UseLocalTime = UseLocalTime,
UseMask = UseMask
};
}
return _loggerSettings;
}
set => _loggerSettings = value;
}
public ILogger CreateLogger(string categoryName)
=> new BatchingLogger(this, categoryName, LoggerSettings);
protected abstract Task WriteMessagesAsync(
IReadOnlyList<LogMessage> messages,
CancellationToken token);
internal string AddMessage(DateTimeOffset timestamp, string message, string category)
{
var log = CreateLogMessage(message, timestamp, category);
_queue.Add(log);
return log.Message;
}
private LogMessage CreateLogMessage(string message, DateTimeOffset ts, string category)
{
if (LoggerSettings.UseMask)
{
SensitiveDataMasker sensitiveDataMasker = new SensitiveDataMasker(LoggerSettings);
message = sensitiveDataMasker.MaskSensitiveInformation(message);
}
return new LogMessage
{
Message = message,
Timestamp = ts,
Category = category
};
}
private async void ProcessLoop()
{
var batch = new List<LogMessage>(_batchSize);
try
{
foreach (var item in _queue.GetConsumingEnumerable(_cts.Token))
{
batch.Add(item);
if (batch.Count < _batchSize)
{
continue;
}
await WriteMessagesAsync(batch, _cts.Token).ConfigureAwait(false);
batch.Clear();
}
if (batch.Count > 0)
{
await WriteMessagesAsync(batch, _cts.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// normal shutdown
}
catch (Exception ex)
{
// last-resort logging
Console.Error.WriteLine(ex);
}
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
_cts.Cancel();
_queue.CompleteAdding();
try { _worker.Wait(); } catch { }
_cts.Dispose();
_queue.Dispose();
}
}