The Serilog async sink has a health monitoring feature, where one must implement IAsyncLogEventSinkMonitor
to monitor when the buffer is overwhelmed and log events are dropped.
The sample shown is pseudocode without much detail. I'm unsure how to implement it.
Here's a simple demo implementation, based on various issues in the repo ([1], [2]).
AsyncSinkMonitor.cs
using Serilog.Debugging;
using Serilog.Sinks.Async;
namespace Demo;
public sealed class AsyncSinkMonitor : IAsyncLogEventSinkMonitor, IDisposable
{
public static AsyncSinkMonitor Instance { get; } = new();
private const int TIMER_PERIOD_MILLISECONDS = 250; // choose carefully; too low would cause perf drop, too high would miss dropped logs
private const double THRESHOLD_WARN = .5;
private const double THRESHOLD_CRIT = .9;
private System.Timers.Timer? _timer;
public void Dispose()
{
if (_timer == null) return;
_timer.Stop();
_timer.Dispose();
_timer = null;
}
public void StartMonitoring(IAsyncLogEventSinkInspector inspector)
{
_timer?.Dispose();
_timer = new System.Timers.Timer(TIMER_PERIOD_MILLISECONDS);
_timer.Elapsed += (sender, args) => OnTimerElapsed(inspector);
_timer.Start();
}
public void StopMonitoring(IAsyncLogEventSinkInspector inspector) =>
Dispose();
private void OnTimerElapsed(IAsyncLogEventSinkInspector inspector)
{
var utilisation = (double)inspector.Count / inspector.BufferSize;
var nDrops = inspector.DroppedMessagesCount; // cumulative value; is never reset to zero
if (THRESHOLD_WARN < utilisation) SelfLog.WriteLine("WARN: Async buffer exceeded {0:p0} utilisation; will drop log events when reaches 100%; dropped {1} since app start", utilisation, nDrops);
else if (THRESHOLD_CRIT < utilisation) SelfLog.WriteLine("CRIT: Async buffer exceeded {0:p0} utilisation; will drop log events when reaches 100%; dropped {1} since app start", utilisation, nDrops);
}
}
appsettings.json
"WriteTo": [
{
"Name": "Async",
"Args": {
"bufferSize": 10000, // 10k is default
"blockWhenFull": "false",
"monitor": "Demo.AsyncSinkMonitor::Instance, Demo",
"configure": [{
// wrapped sink...
}]
}
}
]
Or in code:
.WriteTo.Async(
x => x.WrappedSink(),
bufferSize: 10_000,
blockWhenFull: false,
monitor: AsyncSinkMonitor.Instance
)
Notes:
2024-11-20T08:13:47.7427088Z Serilog.Sinks.Async.BackgroundWorkerSink: unable to enqueue, capacity 10000 (Permanent, 1 events)