@@ -0,0 +1,32 @@
|
||||
using System.Threading.Channels;
|
||||
using ROLAC.API.Entities.Logging;
|
||||
|
||||
namespace ROLAC.API.Services.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// A singleton, bounded in-memory queue decoupling log producers (the ILogger hot path, the
|
||||
/// audit interceptor, singleton services) from the single background DB writer. Enqueue is a
|
||||
/// non-blocking <c>TryWrite</c>; when full the OLDEST entry is dropped (DropWrite) rather than
|
||||
/// blocking a request thread or throwing — logging must never throw or stall business code.
|
||||
/// Carries both SystemLog and AuditLog rows via a small union envelope.
|
||||
/// </summary>
|
||||
public sealed class SystemLogQueue
|
||||
{
|
||||
private readonly Channel<LogEnvelope> _channel =
|
||||
Channel.CreateBounded<LogEnvelope>(new BoundedChannelOptions(capacity: 4096)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.DropWrite,
|
||||
SingleReader = true,
|
||||
SingleWriter = false,
|
||||
});
|
||||
|
||||
public bool TryEnqueue(SystemLog log) => _channel.Writer.TryWrite(new LogEnvelope(log, null));
|
||||
|
||||
public bool TryEnqueue(AuditLog log) => _channel.Writer.TryWrite(new LogEnvelope(null, log));
|
||||
|
||||
public IAsyncEnumerable<LogEnvelope> ReadAllAsync(CancellationToken cancellationToken) =>
|
||||
_channel.Reader.ReadAllAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>Either a SystemLog or an AuditLog — exactly one is non-null.</summary>
|
||||
public sealed record LogEnvelope(SystemLog? System, AuditLog? Audit);
|
||||
Reference in New Issue
Block a user