diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..ae3a3c9
Binary files /dev/null and b/.DS_Store differ
diff --git a/Build.ps1 b/Build.ps1
index 3c41f46..6ded5be 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -29,7 +29,7 @@ foreach ($src in ls src/*) {
} else {
& dotnet pack -c Release -o ..\..\artifacts --no-build
}
- if($LASTEXITCODE -ne 0) { exit 1 }
+ if($LASTEXITCODE -ne 0) { throw "build failed" }
Pop-Location
}
@@ -40,7 +40,7 @@ foreach ($test in ls test/*.Tests) {
echo "build: Testing project in $test"
& dotnet test -c Release
- if($LASTEXITCODE -ne 0) { exit 3 }
+ if($LASTEXITCODE -ne 0) { throw "tests failed" }
Pop-Location
}
diff --git a/README.md b/README.md
index 6b09740..a023537 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Serilog.Sinks.Async [![Build status](https://ci.appveyor.com/api/projects/status/gvk0wl7aows14spn?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-async) [![NuGet](https://img.shields.io/nuget/v/Serilog.Sinks.Async.svg)](https://www.nuget.org/packages/Serilog.Sinks.Async) [![Join the chat at https://gitter.im/serilog/serilog](https://img.shields.io/gitter/room/serilog/serilog.svg)](https://gitter.im/serilog/serilog)
+# Serilog.Sinks.Async [![Build status](https://ci.appveyor.com/api/projects/status/gvk0wl7aows14spn?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-async) [![NuGet](https://img.shields.io/nuget/v/Serilog.Sinks.Async.svg)](https://www.nuget.org/packages/Serilog.Sinks.Async)
An asynchronous wrapper for other [Serilog](https://serilog.net) sinks. Use this sink to reduce the overhead of logging calls by delegating work to a background thread. This is especially suited to non-batching sinks like the [File](https://github.com/serilog/serilog-sinks-file) and [RollingFile](https://github.com/serilog/serilog-sinks-rollingfile) sinks that may be affected by I/O bottlenecks.
@@ -8,8 +8,8 @@ An asynchronous wrapper for other [Serilog](https://serilog.net) sinks. Use this
Install from [NuGet](https://nuget.org/packages/serilog.sinks.async):
-```powershell
-Install-Package Serilog.Sinks.Async
+```sh
+dotnet add package Serilog.Sinks.Async
```
Assuming you have already installed the target sink, such as the file sink, move the wrapped sink's configuration within a `WriteTo.Async()` statement:
diff --git a/src/Serilog.Sinks.Async/Serilog.Sinks.Async.csproj b/src/Serilog.Sinks.Async/Serilog.Sinks.Async.csproj
index 6b2733f..db578f6 100644
--- a/src/Serilog.Sinks.Async/Serilog.Sinks.Async.csproj
+++ b/src/Serilog.Sinks.Async/Serilog.Sinks.Async.csproj
@@ -2,7 +2,7 @@
Asynchronous sink wrapper for Serilog.
- 2.0.0
+ 2.1.0
Jezz Santos;Serilog Contributors
net471;net462
@@ -29,7 +29,7 @@
-
+
diff --git a/src/Serilog.Sinks.Async/Sinks/Async/BackgroundWorkerSink.cs b/src/Serilog.Sinks.Async/Sinks/Async/BackgroundWorkerSink.cs
index e755cf3..f7c16af 100644
--- a/src/Serilog.Sinks.Async/Sinks/Async/BackgroundWorkerSink.cs
+++ b/src/Serilog.Sinks.Async/Sinks/Async/BackgroundWorkerSink.cs
@@ -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 _queue;
readonly Task _worker;
readonly IAsyncLogEventSinkMonitor? _monitor;
+
+ // By contract, set only during initialization, so updates are not synchronized.
+ ILoggingFailureListener _failureListener = SelfLog.FailureListener;
long _droppedMessages;
@@ -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
{
@@ -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);
}
}
@@ -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);
}
}
@@ -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));
+ }
}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.Async.Tests/BackgroundWorkerSinkTests.cs b/test/Serilog.Sinks.Async.Tests/BackgroundWorkerSinkTests.cs
index 50b6ce4..bd006c2 100644
--- a/test/Serilog.Sinks.Async.Tests/BackgroundWorkerSinkTests.cs
+++ b/test/Serilog.Sinks.Async.Tests/BackgroundWorkerSinkTests.cs
@@ -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;
@@ -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(exception);
+ }
}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.Async.Tests/Serilog.Sinks.Async.Tests.csproj b/test/Serilog.Sinks.Async.Tests/Serilog.Sinks.Async.Tests.csproj
index 644e6db..4dfe00a 100644
--- a/test/Serilog.Sinks.Async.Tests/Serilog.Sinks.Async.Tests.csproj
+++ b/test/Serilog.Sinks.Async.Tests/Serilog.Sinks.Async.Tests.csproj
@@ -6,6 +6,7 @@
../../assets/Serilog.snk
true
true
+ 12
diff --git a/test/Serilog.Sinks.Async.Tests/Support/CollectingFailureListener.cs b/test/Serilog.Sinks.Async.Tests/Support/CollectingFailureListener.cs
new file mode 100644
index 0000000..e35cc51
--- /dev/null
+++ b/test/Serilog.Sinks.Async.Tests/Support/CollectingFailureListener.cs
@@ -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 _events = [];
+ readonly List _exceptions = [];
+
+ public IReadOnlyList Events
+ {
+ get
+ {
+ lock (_sync)
+ return _events.ToList();
+ }
+ }
+ public IReadOnlyList Exceptions
+ {
+ get
+ {
+ lock (_sync)
+ return _exceptions.ToList();
+ }
+ }
+
+ public void OnLoggingFailed(object sender, LoggingFailureKind kind, string message, IReadOnlyCollection events,
+ Exception exception)
+ {
+ lock (_sync)
+ {
+ if (exception != null)
+ _exceptions.Add(exception);
+
+ foreach (var logEvent in events ?? [])
+ {
+ _events.Add(logEvent);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.Async.Tests/Support/NotImplementedSink.cs b/test/Serilog.Sinks.Async.Tests/Support/NotImplementedSink.cs
new file mode 100644
index 0000000..e0898c8
--- /dev/null
+++ b/test/Serilog.Sinks.Async.Tests/Support/NotImplementedSink.cs
@@ -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();
+ }
+}
\ No newline at end of file