156 lines
4.3 KiB
C#
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();
|
|
}
|
|
}
|