Skip to content

Commit

Permalink
Merge pull request #98 from nblumhardt/failure-listeners
Browse files Browse the repository at this point in the history
Support `ILoggingFailureListener` and fallback sinks
  • Loading branch information
nblumhardt authored Oct 21, 2024
2 parents a0786d4 + e2bbf9c commit f934784
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 8 deletions.
Binary file added .DS_Store
Binary file not shown.
4 changes: 2 additions & 2 deletions src/Serilog.Sinks.Async/Serilog.Sinks.Async.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<Description>Asynchronous sink wrapper for Serilog.</Description>
<VersionPrefix>2.0.1</VersionPrefix>
<VersionPrefix>2.1.0</VersionPrefix>
<Authors>Jezz Santos;Serilog Contributors</Authors>
<!-- .NET Framework version targeting is frozen at these two TFMs. -->
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT'">net471;net462</TargetFrameworks>
Expand All @@ -29,7 +29,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog" Version="4.0.0" />
<PackageReference Include="Serilog" Version="4.1.0-*" />
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="all" />
</ItemGroup>

Expand Down
22 changes: 17 additions & 5 deletions src/Serilog.Sinks.Async/Sinks/Async/BackgroundWorkerSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@

namespace Serilog.Sinks.Async;

sealed class BackgroundWorkerSink : ILogEventSink, IAsyncLogEventSinkInspector, IDisposable
sealed class BackgroundWorkerSink : ILogEventSink, IAsyncLogEventSinkInspector, IDisposable, ISetLoggingFailureListener
{
readonly ILogEventSink _wrappedSink;
readonly bool _blockWhenFull;
readonly BlockingCollection<LogEvent> _queue;
readonly Task _worker;
readonly IAsyncLogEventSinkMonitor? _monitor;

// By contract, set only during initialization, so updates are not synchronized.
ILoggingFailureListener _failureListener = SelfLog.FailureListener;

long _droppedMessages;

Expand All @@ -46,7 +49,10 @@ public BackgroundWorkerSink(ILogEventSink wrappedSink, int bufferCapacity, bool
public void Emit(LogEvent logEvent)
{
if (_queue.IsAddingCompleted)
{
_failureListener.OnLoggingFailed(this, LoggingFailureKind.Final, "the sink has been disposed", [logEvent], null);
return;
}

try
{
Expand All @@ -59,14 +65,15 @@ public void Emit(LogEvent logEvent)
if (!_queue.TryAdd(logEvent))
{
Interlocked.Increment(ref _droppedMessages);
SelfLog.WriteLine("{0} unable to enqueue, capacity {1}", typeof(BackgroundWorkerSink), _queue.BoundedCapacity);
_failureListener.OnLoggingFailed(this, LoggingFailureKind.Permanent, $"unable to enqueue, capacity {_queue.BoundedCapacity}", [logEvent], null);
}
}
}
catch (InvalidOperationException)
catch (InvalidOperationException ex)
{
// Thrown in the event of a race condition when we try to add another event after
// CompleteAdding has been called
_failureListener.OnLoggingFailed(this, LoggingFailureKind.Final, "the sink has been disposed", [logEvent], ex);
}
}

Expand Down Expand Up @@ -95,13 +102,13 @@ void Pump()
}
catch (Exception ex)
{
SelfLog.WriteLine("{0} failed to emit event to wrapped sink: {1}", typeof(BackgroundWorkerSink), ex);
_failureListener.OnLoggingFailed(this, LoggingFailureKind.Permanent, "failed to emit event to wrapped sink", [next], ex);
}
}
}
catch (Exception fatal)
{
SelfLog.WriteLine("{0} fatal error in worker thread: {1}", typeof(BackgroundWorkerSink), fatal);
_failureListener.OnLoggingFailed(this, LoggingFailureKind.Final, "fatal error in worker thread", null, fatal);
}
}

Expand All @@ -110,4 +117,9 @@ void Pump()
int IAsyncLogEventSinkInspector.Count => _queue.Count;

long IAsyncLogEventSinkInspector.DroppedMessagesCount => _droppedMessages;

public void SetFailureListener(ILoggingFailureListener failureListener)
{
_failureListener = failureListener ?? throw new ArgumentNullException(nameof(failureListener));
}
}
19 changes: 18 additions & 1 deletion test/Serilog.Sinks.Async.Tests/BackgroundWorkerSinkTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Serilog.Sinks.Async.Tests.Support;
using System;
using Serilog.Events;
using Serilog.Sinks.Async.Tests.Support;
using Xunit;

namespace Serilog.Sinks.Async.Tests;
Expand Down Expand Up @@ -50,4 +52,19 @@ public void CtorAndDisposeInformMonitor()

Assert.Null(monitor.Inspector);
}

[Fact]
public void SupportsLoggingFailureListener()
{
var failureListener = new CollectingFailureListener();
var sink = new BackgroundWorkerSink(new NotImplementedSink(), 1, false, null);
sink.SetFailureListener(failureListener);
var evt = new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null, MessageTemplate.Empty, []);
sink.Emit(evt);
sink.Dispose();
var collected = Assert.Single(failureListener.Events);
Assert.Same(evt, collected);
var exception = Assert.Single(failureListener.Exceptions);
Assert.IsType<NotImplementedException>(exception);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<AssemblyOriginatorKeyFile>../../assets/Serilog.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<LangVersion>12</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Serilog.Core;
using Serilog.Events;

namespace Serilog.Sinks.Async.Tests.Support;

class CollectingFailureListener: ILoggingFailureListener
{
readonly object _sync = new();
readonly List<LogEvent> _events = [];
readonly List<Exception> _exceptions = [];

public IReadOnlyList<LogEvent> Events
{
get
{
lock (_sync)
return _events.ToList();
}
}
public IReadOnlyList<Exception> Exceptions
{
get
{
lock (_sync)
return _exceptions.ToList();
}
}

public void OnLoggingFailed(object sender, LoggingFailureKind kind, string message, IReadOnlyCollection<LogEvent> events,
Exception exception)
{
lock (_sync)
{
if (exception != null)
_exceptions.Add(exception);

foreach (var logEvent in events ?? [])
{
_events.Add(logEvent);
}
}
}
}
13 changes: 13 additions & 0 deletions test/Serilog.Sinks.Async.Tests/Support/NotImplementedSink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using Serilog.Core;
using Serilog.Events;

namespace Serilog.Sinks.Async.Tests.Support;

class NotImplementedSink: ILogEventSink
{
public void Emit(LogEvent logEvent)
{
throw new NotImplementedException();
}
}

0 comments on commit f934784

Please sign in to comment.