Skip to content

Commit

Permalink
Add IAsyncLogEventSinkMonitor
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed Apr 24, 2018
1 parent 510db8d commit 6546958
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 4 deletions.
32 changes: 30 additions & 2 deletions src/Serilog.Sinks.Async/LoggerConfigurationAsyncExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,38 @@ public static LoggerConfiguration Async(
Action<LoggerSinkConfiguration> configure,
int bufferSize = 10000,
bool blockWhenFull = false)
{
return loggerSinkConfiguration.Async(configure, null, bufferSize, blockWhenFull);
}

/// <summary>
/// Configure a sink to be invoked asynchronously, on a background worker thread.
/// Accepts a reference to a <paramref name="monitor"/> that will be supplied the internal state interface for health monitoring purposes.
/// </summary>
/// <param name="loggerSinkConfiguration">The <see cref="LoggerSinkConfiguration"/> being configured.</param>
/// <param name="configure">An action that configures the wrapped sink.</param>
/// <param name="bufferSize">The size of the concurrent queue used to feed the background worker thread. If
/// the thread is unable to process events quickly enough and the queue is filled, depending on
/// <paramref name="blockWhenFull"/> the queue will block or subsequent events will be dropped until
/// room is made in the queue.</param>
/// <param name="blockWhenFull">Block when the queue is full, instead of dropping events.</param>
/// <param name="monitor">Monitor to supply buffer information to. If the monitor implements <see cref="IDisposable"/>, <c>Dispose()</c> will be called to advise of the Sink being <c>Dispose()</c>d.</param>
/// <returns>A <see cref="LoggerConfiguration"/> allowing configuration to continue.</returns>
public static LoggerConfiguration Async(
this LoggerSinkConfiguration loggerSinkConfiguration,
Action<LoggerSinkConfiguration> configure,
IAsyncLogEventSinkMonitor monitor,
int bufferSize,
bool blockWhenFull)
{
return LoggerSinkConfiguration.Wrap(
loggerSinkConfiguration,
wrappedSink => new BackgroundWorkerSink(wrappedSink, bufferSize, blockWhenFull),
wrappedSink =>
{
var sink = new BackgroundWorkerSink(wrappedSink, bufferSize, blockWhenFull, monitor);
monitor?.MonitorState(sink);
return sink;
},
configure);
}

Expand Down Expand Up @@ -77,7 +105,7 @@ public static LoggerConfiguration Async(
loggerSinkConfiguration,
wrappedSink =>
{
var sink = new BackgroundWorkerSink(wrappedSink, bufferSize, blockWhenFull);
var sink = new BackgroundWorkerSink(wrappedSink, bufferSize, blockWhenFull, null);
stateLens = sink;
return sink;
},
Expand Down
6 changes: 5 additions & 1 deletion src/Serilog.Sinks.Async/Sinks/Async/BackgroundWorkerSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ sealed class BackgroundWorkerSink : ILogEventSink, IAsyncLogEventSinkState, IDis
readonly bool _blockWhenFull;
readonly BlockingCollection<LogEvent> _queue;
readonly Task _worker;
readonly IAsyncLogEventSinkMonitor _monitor;

long _droppedMessages;

public BackgroundWorkerSink(ILogEventSink pipeline, int bufferCapacity, bool blockWhenFull)
public BackgroundWorkerSink(ILogEventSink pipeline, int bufferCapacity, bool blockWhenFull, IAsyncLogEventSinkMonitor monitor = null)
{
if (bufferCapacity <= 0) throw new ArgumentOutOfRangeException(nameof(bufferCapacity));
_pipeline = pipeline ?? throw new ArgumentNullException(nameof(pipeline));
_blockWhenFull = blockWhenFull;
_queue = new BlockingCollection<LogEvent>(bufferCapacity);
_worker = Task.Factory.StartNew(Pump, CancellationToken.None, TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
_monitor = monitor;
}

public void Emit(LogEvent logEvent)
Expand Down Expand Up @@ -62,6 +64,8 @@ public void Dispose()
_worker.Wait();

(_pipeline as IDisposable)?.Dispose();

(_monitor as IDisposable)?.Dispose();
}

void Pump()
Expand Down
15 changes: 15 additions & 0 deletions src/Serilog.Sinks.Async/Sinks/Async/IAsyncLogEventSinkMonitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Serilog.Sinks.Async
{
/// <summary>
/// Defines a mechanism for the Async Sink to provide buffer metadata to facilitate integration into system health checking.
/// </summary>
/// <remarks>If the instance implements <see cref="System.IDisposable"/>, it will be <c>Dispose()</c>d at then time the Sink is.</remarks>
public interface IAsyncLogEventSinkMonitor
{
/// <summary>
/// Invoked by Sink to supply the buffer state hook to the monitor.
/// </summary>
/// <param name="state">The Async Sink's state information interface.</param>
void MonitorState(IAsyncLogEventSinkState state);
}
}
2 changes: 1 addition & 1 deletion test/Serilog.Sinks.Async.Tests/BackgroundWorkerSinkSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public BackgroundWorkerSinkSpec()
[Fact]
public void WhenCtorWithNullSink_ThenThrows()
{
Assert.Throws<ArgumentNullException>(() => new BackgroundWorkerSink(null, 10000, false));
Assert.Throws<ArgumentNullException>(() => new BackgroundWorkerSink(null, 10000, false, null));
}

[Fact]
Expand Down

0 comments on commit 6546958

Please sign in to comment.