1

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.

1 Answer 1

2

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:

  • It writes to Serilog's "self log", but one could write to a file or elsewhere
  • Choose the timer's period carefully: too low would cause a perf drop, too high would miss dropped logs; intuitively, a value in the range 250..500ms is probably fine
  • Even without above code, the background worker logs to the self log anyway, as follows:

    2024-11-20T08:13:47.7427088Z Serilog.Sinks.Async.BackgroundWorkerSink: unable to enqueue, capacity 10000 (Permanent, 1 events)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.