33 lines
1.4 KiB
C#
33 lines
1.4 KiB
C#
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);
|