diff --git a/.gitignore b/.gitignore
index c57a1e9..d7c7158 100644
--- a/.gitignore
+++ b/.gitignore
@@ -239,3 +239,4 @@ BenchmarkDotNet.Artifacts/
.idea
*.orig
+.DS_Store/
diff --git a/Build.ps1 b/Build.ps1
index b9ed918..e482fe3 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -33,7 +33,7 @@ foreach ($src in Get-ChildItem src/*) {
} else {
& dotnet pack -c Release --no-build -o ..\..\artifacts
}
- if($LASTEXITCODE -ne 0) { exit 1 }
+ if($LASTEXITCODE -ne 0) { throw "Failed" }
Pop-Location
}
@@ -44,18 +44,7 @@ foreach ($test in Get-ChildItem test/*.Tests) {
Write-Output "build: Testing project in $test"
& dotnet test -c Release
- if($LASTEXITCODE -ne 0) { exit 3 }
-
- Pop-Location
-}
-
-foreach ($test in Get-ChildItem test/*.PerformanceTests) {
- Push-Location $test
-
- Write-Output "build: Building performance test project in $test"
-
- & dotnet build -c Release
- if($LASTEXITCODE -ne 0) { exit 2 }
+ if($LASTEXITCODE -ne 0) { throw "Failed" }
Pop-Location
}
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..b6fe6cd
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,20 @@
+
+
+
+
+ latest
+ True
+ true
+ ../../assets/Serilog.snk
+ true
+ false
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 0000000..6132714
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index c04c69b..45a9f4f 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -9,7 +9,7 @@ artifacts:
deploy:
- provider: NuGet
api_key:
- secure: oemq1E4zMR+LKQyrR83ZLcugPpZtl5OMKjtpMy/mbPEwuFGS+Oe46427D9KoHYD8
+ secure: sDnchSg4TZIOK7oIUI6BJwFPNENTOZrGNsroGO1hehLJSvlHpFmpTwiX8+bgPD+Q
skip_symbols: true
on:
branch: /^(main|dev)$/
diff --git a/global.json b/global.json
index ba75026..ec30e8e 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "6.0.300",
+ "version": "8.0.100",
"allowPrerelease": false,
"rollForward": "latestFeature"
}
diff --git a/serilog-sinks-periodicbatching.sln b/serilog-sinks-periodicbatching.sln
index f92a8b8..ef32185 100644
--- a/serilog-sinks-periodicbatching.sln
+++ b/serilog-sinks-periodicbatching.sln
@@ -17,8 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.PeriodicBatch
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.PeriodicBatching.Tests", "test\Serilog.Sinks.PeriodicBatching.Tests\Serilog.Sinks.PeriodicBatching.Tests.csproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.PeriodicBatching.PerformanceTests", "test\Serilog.Sinks.PeriodicBatching.PerformanceTests\Serilog.Sinks.PeriodicBatching.PerformanceTests.csproj", "{80B760D1-3862-49AD-9D72-23608550C318}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sln", "sln", "{E49CF29C-7646-4E9E-82C6-BF81A76A116F}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@@ -29,8 +27,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sln", "sln", "{E49CF29C-764
README.md = README.md
RunPerfTests.ps1 = RunPerfTests.ps1
global.json = global.json
+ Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestHarness", "test\TestHarness\TestHarness.csproj", "{20A1F97F-3EEB-4D9B-981C-20818228FAC9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -45,10 +46,10 @@ Global
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.Build.0 = Release|Any CPU
- {80B760D1-3862-49AD-9D72-23608550C318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {80B760D1-3862-49AD-9D72-23608550C318}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {80B760D1-3862-49AD-9D72-23608550C318}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {80B760D1-3862-49AD-9D72-23608550C318}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20A1F97F-3EEB-4D9B-981C-20818228FAC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {20A1F97F-3EEB-4D9B-981C-20818228FAC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20A1F97F-3EEB-4D9B-981C-20818228FAC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {20A1F97F-3EEB-4D9B-981C-20818228FAC9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -56,6 +57,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{324C2F52-D9F7-4844-9BC4-9906E228D380} = {037440DE-440B-4129-9F7A-09B42D00397E}
{3C2D8E01-5580-426A-BDD9-EC59CD98E618} = {F6E07A13-B9D3-4019-B25A-DE1F6C17E108}
- {80B760D1-3862-49AD-9D72-23608550C318} = {F6E07A13-B9D3-4019-B25A-DE1F6C17E108}
+ {20A1F97F-3EEB-4D9B-981C-20818228FAC9} = {F6E07A13-B9D3-4019-B25A-DE1F6C17E108}
EndGlobalSection
EndGlobal
diff --git a/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs b/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs
index ff8192e..499c93b 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs
@@ -1,9 +1,6 @@
-using System;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.CompilerServices;
-[assembly: AssemblyVersion("3.0.0.0")]
-
[assembly: CLSCompliant(true)]
[assembly: InternalsVisibleTo("Serilog.Sinks.PeriodicBatching.Tests, PublicKey=" +
diff --git a/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj b/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj
index 4862b19..b2b628d 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj
+++ b/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj
@@ -2,51 +2,34 @@
Buffer batches of log events to be flushed asynchronously.
- 3.1.0
+ 4.0.0
Serilog Contributors
- net45;netstandard1.1;netstandard1.2;netstandard2.0;netstandard2.1
+ net462
+ $(TargetFrameworks);netstandard2.0;net6.0
true
Serilog
- ../../assets/Serilog.snk
- true
- true
serilog;batching;timer
icon.png
Apache-2.0
https://github.com/serilog/serilog-sinks-periodicbatching
https://github.com/serilog/serilog-sinks-periodicbatching
git
- false
- latest
- True
- false
enable
+ README.md
-
- $(DefineConstants);FEATURE_THREADING_TIMER
-
-
-
- $(DefineConstants);FEATURE_THREADING_TIMER;FEATURE_EXECUTION_CONTEXT
-
-
-
- $(DefineConstants);FEATURE_THREADING_TIMER;FEATURE_EXECUTION_CONTEXT;FEATURE_ASYNCDISPOSABLE
+
+ $(DefineConstants);FEATURE_ASYNCDISPOSABLE
-
-
-
-
-
-
-
+
+
+
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BatchedConnectionStatus.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BatchedConnectionStatus.cs
deleted file mode 100644
index 5a3eda4..0000000
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BatchedConnectionStatus.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright © Serilog Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using System;
-
-namespace Serilog.Sinks.PeriodicBatching
-{
- ///
- /// Manages reconnection period and transient fault response for .
- /// During normal operation an object of this type will simply echo the configured batch transmission
- /// period. When availability fluctuates, the class tracks the number of failed attempts, each time
- /// increasing the interval before reconnection is attempted (up to a set maximum) and at predefined
- /// points indicating that either the current batch, or entire waiting queue, should be dropped. This
- /// Serves two purposes - first, a loaded receiver may need a temporary reduction in traffic while coming
- /// back online. Second, the sender needs to account for both bad batches (the first fault response) and
- /// also overproduction (the second, queue-dropping response). In combination these should provide a
- /// reasonable delivery effort but ultimately protect the sender from memory exhaustion.
- ///
- ///
- /// Currently used only by , but may
- /// provide the basis for a "smart" exponential backoff timer. There are other factors to consider
- /// including the desire to send batches "when full" rather than continuing to buffer, and so-on.
- ///
- class BatchedConnectionStatus
- {
- static readonly TimeSpan MinimumBackoffPeriod = TimeSpan.FromSeconds(5);
- static readonly TimeSpan MaximumBackoffInterval = TimeSpan.FromMinutes(10);
-
- const int FailuresBeforeDroppingBatch = 8;
- const int FailuresBeforeDroppingQueue = 10;
-
- readonly TimeSpan _period;
-
- int _failuresSinceSuccessfulBatch;
-
- public BatchedConnectionStatus(TimeSpan period)
- {
- if (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period), "The batching period must be a positive timespan");
-
- _period = period;
- }
-
- public void MarkSuccess()
- {
- _failuresSinceSuccessfulBatch = 0;
- }
-
- public void MarkFailure()
- {
- ++_failuresSinceSuccessfulBatch;
- }
-
- public TimeSpan NextInterval
- {
- get
- {
- // Available, and first failure, just try the batch interval
- if (_failuresSinceSuccessfulBatch <= 1) return _period;
-
- // Second failure, start ramping up the interval - first 2x, then 4x, ...
- var backoffFactor = Math.Pow(2, (_failuresSinceSuccessfulBatch - 1));
-
- // If the period is ridiculously short, give it a boost so we get some
- // visible backoff.
- var backoffPeriod = Math.Max(_period.Ticks, MinimumBackoffPeriod.Ticks);
-
- // The "ideal" interval
- var backedOff = (long) (backoffPeriod * backoffFactor);
-
- // Capped to the maximum interval
- var cappedBackoff = Math.Min(MaximumBackoffInterval.Ticks, backedOff);
-
- // Unless that's shorter than the period, in which case we'll just apply the period
- var actual = Math.Max(_period.Ticks, cappedBackoff);
-
- return TimeSpan.FromTicks(actual);
- }
- }
-
- public bool ShouldDropBatch => _failuresSinceSuccessfulBatch >= FailuresBeforeDroppingBatch;
-
- public bool ShouldDropQueue => _failuresSinceSuccessfulBatch >= FailuresBeforeDroppingQueue;
- }
-}
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
deleted file mode 100644
index 23ca933..0000000
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright © Serilog Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using System;
-using System.Collections.Concurrent;
-using System.Threading;
-
-namespace Serilog.Sinks.PeriodicBatching
-{
- class BoundedConcurrentQueue
- {
- const int Unbounded = -1;
-
- readonly ConcurrentQueue _queue = new();
- readonly int _queueLimit;
-
- int _counter;
-
- public BoundedConcurrentQueue(int? queueLimit = null)
- {
- if (queueLimit.HasValue && queueLimit <= 0)
- throw new ArgumentOutOfRangeException(nameof(queueLimit), "Queue limit must be positive, or `null` to indicate unbounded.");
-
- _queueLimit = queueLimit ?? Unbounded;
- }
-
- public int Count => _queue.Count;
-
- public bool TryDequeue(out T item)
- {
- if (_queueLimit == Unbounded)
- return _queue.TryDequeue(out item);
-
- var result = false;
- try
- { }
- finally // prevent state corrupt while aborting
- {
- if (_queue.TryDequeue(out item))
- {
- Interlocked.Decrement(ref _counter);
- result = true;
- }
- }
-
- return result;
- }
-
- public bool TryEnqueue(T item)
- {
- if (_queueLimit == Unbounded)
- {
- _queue.Enqueue(item);
- return true;
- }
-
- var result = true;
- try
- { }
- finally
- {
- if (Interlocked.Increment(ref _counter) <= _queueLimit)
- {
- _queue.Enqueue(item);
- }
- else
- {
- Interlocked.Decrement(ref _counter);
- result = false;
- }
- }
-
- return result;
- }
- }
-}
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/FailureAwareBatchScheduler.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/FailureAwareBatchScheduler.cs
new file mode 100644
index 0000000..8b6366f
--- /dev/null
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/FailureAwareBatchScheduler.cs
@@ -0,0 +1,88 @@
+// Copyright © Serilog Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Serilog.Sinks.PeriodicBatching;
+
+///
+/// Manages reconnection period and transient fault response for .
+/// During normal operation an object of this type will simply echo the configured batch transmission
+/// period. When availability fluctuates, the class tracks the number of failed attempts, each time
+/// increasing the interval before reconnection is attempted (up to a set maximum) and at predefined
+/// points indicating that either the current batch, or entire waiting queue, should be dropped. This
+/// Serves two purposes - first, a loaded receiver may need a temporary reduction in traffic while coming
+/// back online. Second, the sender needs to account for both bad batches (the first fault response) and
+/// also overproduction (the second, queue-dropping response). In combination these should provide a
+/// reasonable delivery effort but ultimately protect the sender from memory exhaustion.
+///
+class FailureAwareBatchScheduler
+{
+ static readonly TimeSpan MinimumBackoffPeriod = TimeSpan.FromSeconds(5);
+ static readonly TimeSpan MaximumBackoffInterval = TimeSpan.FromMinutes(10);
+
+ const int FailuresBeforeDroppingBatch = 8;
+ const int FailuresBeforeDroppingQueue = 10;
+
+ readonly TimeSpan _period;
+
+ int _failuresSinceSuccessfulBatch;
+
+ public FailureAwareBatchScheduler(TimeSpan period)
+ {
+ if (period < TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException(nameof(period), "The batching period must be a positive timespan.");
+
+ _period = period;
+ }
+
+ public void MarkSuccess()
+ {
+ _failuresSinceSuccessfulBatch = 0;
+ }
+
+ public void MarkFailure()
+ {
+ ++_failuresSinceSuccessfulBatch;
+ }
+
+ public TimeSpan NextInterval
+ {
+ get
+ {
+ // Available, and first failure, just try the batch interval
+ if (_failuresSinceSuccessfulBatch <= 1) return _period;
+
+ // Second failure, start ramping up the interval - first 2x, then 4x, ...
+ var backoffFactor = Math.Pow(2, _failuresSinceSuccessfulBatch - 1);
+
+ // If the period is ridiculously short, give it a boost so we get some
+ // visible backoff.
+ var backoffPeriod = Math.Max(_period.Ticks, MinimumBackoffPeriod.Ticks);
+
+ // The "ideal" interval
+ var backedOff = (long) (backoffPeriod * backoffFactor);
+
+ // Capped to the maximum interval
+ var cappedBackoff = Math.Min(MaximumBackoffInterval.Ticks, backedOff);
+
+ // Unless that's shorter than the period, in which case we'll just apply the period
+ var actual = Math.Max(_period.Ticks, cappedBackoff);
+
+ return TimeSpan.FromTicks(actual);
+ }
+ }
+
+ public bool ShouldDropBatch => _failuresSinceSuccessfulBatch >= FailuresBeforeDroppingBatch;
+
+ public bool ShouldDropQueue => _failuresSinceSuccessfulBatch >= FailuresBeforeDroppingQueue;
+}
\ No newline at end of file
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs
index e4672c9..1988247 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs
@@ -12,27 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using System.Collections.Generic;
-using System.Threading.Tasks;
using Serilog.Events;
-namespace Serilog.Sinks.PeriodicBatching
+namespace Serilog.Sinks.PeriodicBatching;
+
+///
+/// Interface for targets that accept events in batches.
+///
+public interface IBatchedLogEventSink
{
///
- /// Interface for targets that accept events in batches.
+ /// Emit a batch of log events, running asynchronously.
///
- public interface IBatchedLogEventSink
- {
- ///
- /// Emit a batch of log events, running asynchronously.
- ///
- /// The batch of events to emit.
- Task EmitBatchAsync(IEnumerable batch);
+ /// The batch of events to emit.
+ Task EmitBatchAsync(IEnumerable batch);
- ///
- /// Allows sinks to perform periodic work without requiring additional threads
- /// or timers (thus avoiding additional flush/shut-down complexity).
- ///
- Task OnEmptyBatchAsync();
- }
-}
+ ///
+ /// Allows sinks to perform periodic work without requiring additional threads
+ /// or timers (thus avoiding additional flush/shut-down complexity).
+ ///
+ Task OnEmptyBatchAsync();
+}
\ No newline at end of file
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
index d56fdbd..78eae64 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
@@ -12,359 +12,272 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
+using System.Threading.Channels;
using Serilog.Core;
using Serilog.Debugging;
using Serilog.Events;
// ReSharper disable UnusedParameter.Global, ConvertIfStatementToConditionalTernaryExpression, MemberCanBePrivate.Global, UnusedMember.Global, VirtualMemberNeverOverridden.Global, ClassWithVirtualMembersNeverInherited.Global, SuspiciousTypeConversion.Global
-namespace Serilog.Sinks.PeriodicBatching
-{
- ///
- /// Buffers log events into batches for background flushing.
- ///
- ///
- /// To avoid unbounded memory growth, events are discarded after attempting
- /// to send a batch, regardless of whether the batch succeeded or not. Implementers
- /// that want to change this behavior need to either implement from scratch, or
- /// embed retry logic in the batch emitting functions.
- ///
- public class PeriodicBatchingSink : ILogEventSink, IDisposable, IBatchedLogEventSink
+namespace Serilog.Sinks.PeriodicBatching;
+
+///
+/// Buffers log events into batches for background flushing.
+///
+public sealed class PeriodicBatchingSink : ILogEventSink, IDisposable
#if FEATURE_ASYNCDISPOSABLE
, IAsyncDisposable
#endif
- {
- ///
- /// Constant used with legacy constructor to indicate that the internal queue shouldn't be limited.
- ///
- [Obsolete("Implement `IBatchedLogEventSink` and use the `PeriodicBatchingSinkOptions` constructor.")]
- public const int NoQueueLimit = -1;
-
- readonly IBatchedLogEventSink _batchedLogEventSink;
- readonly int _batchSizeLimit;
- readonly bool _eagerlyEmitFirstEvent;
- readonly BoundedConcurrentQueue _queue;
- readonly BatchedConnectionStatus _status;
- readonly Queue _waitingBatch = new();
-
- readonly object _stateLock = new();
-
- readonly PortableTimer _timer;
-
- bool _unloading;
- bool _started;
-
- ///
- /// Construct a .
- ///
- /// A to send log event batches to. Batches and empty
- /// batch notifications will not be sent concurrently. When the is disposed,
- /// it will dispose this object if possible.
- /// Options controlling behavior of the sink.
- public PeriodicBatchingSink(IBatchedLogEventSink batchedSink, PeriodicBatchingSinkOptions options)
- : this(options)
- {
- _batchedLogEventSink = batchedSink ?? throw new ArgumentNullException(nameof(batchedSink));
- }
-
- ///
- /// Construct a . New code should avoid subclassing
- /// and use
- ///
- /// instead.
- ///
- /// The maximum number of events to include in a single batch.
- /// The time to wait between checking for event batches.
- [Obsolete("Implement `IBatchedLogEventSink` and use the `PeriodicBatchingSinkOptions` constructor.")]
- protected PeriodicBatchingSink(int batchSizeLimit, TimeSpan period)
- : this(new PeriodicBatchingSinkOptions
- {
- BatchSizeLimit = batchSizeLimit,
- Period = period,
- EagerlyEmitFirstEvent = true,
- QueueLimit = null
- })
- {
- _batchedLogEventSink = this;
- }
+{
+ // Buffers events from the write- to the read side.
+ readonly Channel _queue;
+
+ // These fields are used by the write side to signal shutdown.
+ // A mutex is required because the queue writer `Complete()` call is not idempotent and will throw if
+ // called multiple times, e.g. via multiple `Dispose()` calls on this sink.
+ readonly object _stateLock = new();
+ // Needed because the read loop needs to observe shutdown even when the target batched (remote) sink is
+ // unable to accept events (preventing the queue from being drained and completion being observed).
+ readonly CancellationTokenSource _shutdownSignal = new();
+ // The write side can wait on this to ensure shutdown has completed.
+ readonly Task _runLoop;
+
+ // Used only by the read side.
+ readonly IBatchedLogEventSink _targetSink;
+ readonly int _batchSizeLimit;
+ readonly bool _eagerlyEmitFirstEvent;
+ readonly FailureAwareBatchScheduler _batchScheduler;
+ readonly Queue _currentBatch = new();
+ readonly Task _waitForShutdownSignal;
- ///
- /// Construct a . New code should avoid subclassing
- /// and use
- ///
- /// instead.
- ///
- /// The maximum number of events to include in a single batch.
- /// The time to wait between checking for event batches.
- /// Maximum number of events in the queue - use for an unbounded queue.
- [Obsolete("Implement `IBatchedLogEventSink` and use the `PeriodicBatchingSinkOptions` constructor.")]
- protected PeriodicBatchingSink(int batchSizeLimit, TimeSpan period, int queueLimit)
- : this(new PeriodicBatchingSinkOptions
- {
- BatchSizeLimit = batchSizeLimit,
- Period = period,
- EagerlyEmitFirstEvent = true,
- QueueLimit = queueLimit == NoQueueLimit ? null : queueLimit
- })
+ ///
+ /// Construct a .
+ ///
+ /// A to send log event batches to. Batches and empty
+ /// batch notifications will not be sent concurrently. When the is disposed,
+ /// it will dispose this object if possible.
+ /// Options controlling behavior of the sink.
+ public PeriodicBatchingSink(IBatchedLogEventSink batchedSink, PeriodicBatchingSinkOptions options)
+ {
+ if (options == null) throw new ArgumentNullException(nameof(options));
+ if (options.BatchSizeLimit <= 0)
+ throw new ArgumentOutOfRangeException(nameof(options), "The batch size limit must be greater than zero.");
+ if (options.Period <= TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException(nameof(options), "The period must be greater than zero.");
+
+ _targetSink = batchedSink ?? throw new ArgumentNullException(nameof(batchedSink));
+ _batchSizeLimit = options.BatchSizeLimit;
+ _queue = options.QueueLimit is { } limit
+ ? Channel.CreateBounded(new BoundedChannelOptions(limit) { SingleReader = true })
+ : Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true });
+ _batchScheduler = new FailureAwareBatchScheduler(options.Period);
+ _eagerlyEmitFirstEvent = options.EagerlyEmitFirstEvent;
+ _waitForShutdownSignal = Task.Delay(Timeout.InfiniteTimeSpan, _shutdownSignal.Token)
+ .ContinueWith(e => e.Exception, TaskContinuationOptions.OnlyOnFaulted);
+
+ // The conditional here is no longer required in .NET 8+ (dotnet/runtime#82912)
+ using (ExecutionContext.IsFlowSuppressed() ? (IDisposable?)null : ExecutionContext.SuppressFlow())
{
- _batchedLogEventSink = this;
+ _runLoop = Task.Run(LoopAsync);
}
+ }
- PeriodicBatchingSink(PeriodicBatchingSinkOptions options)
- {
- if (options == null) throw new ArgumentNullException(nameof(options));
-
- if (options.BatchSizeLimit <= 0)
- throw new ArgumentOutOfRangeException(nameof(options), "The batch size limit must be greater than zero.");
- if (options.Period <= TimeSpan.Zero)
- throw new ArgumentOutOfRangeException(nameof(options), "The period must be greater than zero.");
+ ///
+ /// Emit the provided log event to the sink. If the sink is being disposed or
+ /// the app domain unloaded, then the event is ignored.
+ ///
+ /// Log event to emit.
+ /// The event is null.
+ ///
+ /// The sink implements the contract that any events whose Emit() method has
+ /// completed at the time of sink disposal will be flushed (or attempted to,
+ /// depending on app domain state).
+ ///
+ public void Emit(LogEvent logEvent)
+ {
+ if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
- _batchSizeLimit = options.BatchSizeLimit;
- _queue = new BoundedConcurrentQueue(options.QueueLimit);
- _status = new BatchedConnectionStatus(options.Period);
- _eagerlyEmitFirstEvent = options.EagerlyEmitFirstEvent;
- _timer = new PortableTimer(_ => OnTick());
+ if (_shutdownSignal.IsCancellationRequested)
+ return;
- // Initialized by externally-callable constructors.
- _batchedLogEventSink = null!;
- }
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- /// 2
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
+ _queue.Writer.TryWrite(logEvent);
+ }
- ///
- /// Free resources held by the sink.
- ///
- /// If true, called because the object is being disposed; if false,
- /// the object is being disposed from the finalizer.
- protected virtual void Dispose(bool disposing)
+ async Task LoopAsync()
+ {
+ var isEagerBatch = _eagerlyEmitFirstEvent;
+ do
{
- if (!disposing) return;
+ // Code from here through to the `try` block is expected to be infallible. It's structured this way because
+ // any failure modes within it haven't been accounted for in the rest of the sink design, and would need
+ // consideration in order for the sink to function robustly (i.e. to avoid hot/infinite looping).
- lock (_stateLock)
- {
- if (_unloading)
- return;
-
- _unloading = true;
- }
-
- _timer.Dispose();
-
- // This is the place where SynchronizationContext.Current is unknown and can be != null
- // so we prevent possible deadlocks here for sync-over-async downstream implementations.
- TaskUtil.ResetSyncContextAndWait(OnTick);
-
- (_batchedLogEventSink as IDisposable)?.Dispose();
- }
-
-#if FEATURE_ASYNCDISPOSABLE
- ///
- public async ValueTask DisposeAsync()
- {
- lock (_stateLock)
+ var fillBatch = Task.Delay(_batchScheduler.NextInterval);
+ do
{
- if (_unloading)
- return;
-
- _unloading = true;
- }
-
- _timer.Dispose();
-
- await OnTick().ConfigureAwait(false);
-
- if (ReferenceEquals(_batchedLogEventSink, this))
- {
- // The sink is being used in the obsolete inheritance-based mode. Old sinks won't
- // override something like `DisposeAsyncCore()`; we just forward to the synchronous
- // `Dispose()` method to ensure whatever cleanup they do still occurs.
- Dispose(true);
- return;
- }
-
- if (_batchedLogEventSink is IAsyncDisposable asyncDisposable)
- await asyncDisposable.DisposeAsync().ConfigureAwait(false);
- else
- (_batchedLogEventSink as IDisposable)?.Dispose();
-
- GC.SuppressFinalize(this);
- }
-#endif
-
- ///
- /// Emit a batch of log events, running to completion synchronously.
- ///
- /// The events to emit.
- /// Override either or ,
- /// not both.
- protected virtual void EmitBatch(IEnumerable events)
- {
- }
+ while (_currentBatch.Count < _batchSizeLimit &&
+ !_shutdownSignal.IsCancellationRequested &&
+ _queue.Reader.TryRead(out var next))
+ {
+ _currentBatch.Enqueue(next);
+ }
+ } while ((_currentBatch.Count < _batchSizeLimit || _currentBatch.Count > 0 && isEagerBatch) &&
+ !_shutdownSignal.IsCancellationRequested &&
+ await TryWaitToReadAsync(_queue.Reader, fillBatch, _shutdownSignal.Token).ConfigureAwait(false));
- ///
- /// Emit a batch of log events, running asynchronously.
- ///
- /// The events to emit.
- /// Override either or ,
- /// not both.
-#pragma warning disable 1998
- protected virtual async Task EmitBatchAsync(IEnumerable events)
-#pragma warning restore 1998
- {
- // ReSharper disable once MethodHasAsyncOverload
- EmitBatch(events);
- }
-
- async Task OnTick()
- {
try
{
- bool batchWasFull;
- do
+ if (_currentBatch.Count == 0)
{
- while (_waitingBatch.Count < _batchSizeLimit &&
- _queue.TryDequeue(out var next))
- {
- _waitingBatch.Enqueue(next);
- }
-
- if (_waitingBatch.Count == 0)
- {
- await _batchedLogEventSink.OnEmptyBatchAsync().ConfigureAwait(false);
- return;
- }
+ await _targetSink.OnEmptyBatchAsync().ConfigureAwait(false);
+ }
+ else
+ {
+ isEagerBatch = false;
- await _batchedLogEventSink.EmitBatchAsync(_waitingBatch).ConfigureAwait(false);
+ await _targetSink.EmitBatchAsync(_currentBatch).ConfigureAwait(false);
- batchWasFull = _waitingBatch.Count >= _batchSizeLimit;
- _waitingBatch.Clear();
- _status.MarkSuccess();
+ _currentBatch.Clear();
+ _batchScheduler.MarkSuccess();
}
- while (batchWasFull); // Otherwise, allow the period to elapse
}
catch (Exception ex)
{
- SelfLog.WriteLine("Exception while emitting periodic batch from {0}: {1}", this, ex);
- _status.MarkFailure();
- }
- finally
- {
- if (_status.ShouldDropBatch)
- _waitingBatch.Clear();
+ WriteToSelfLog("failed emitting a batch", ex);
+ _batchScheduler.MarkFailure();
- if (_status.ShouldDropQueue)
+ if (_batchScheduler.ShouldDropBatch)
{
- while (_queue.TryDequeue(out _)) { }
+ WriteToSelfLog("dropping the current batch");
+ _currentBatch.Clear();
}
- lock (_stateLock)
+ if (_batchScheduler.ShouldDropQueue)
{
- if (!_unloading)
- SetTimer(_status.NextInterval);
+ WriteToSelfLog("dropping all queued events");
+
+ // Not ideal, uses some CPU capacity unnecessarily and doesn't complete in bounded time. The goal is
+ // to reduce memory pressure on the client if the server is offline for extended periods. May be
+ // worth reviewing and possibly abandoning this.
+ while (_queue.Reader.TryRead(out _) && !_shutdownSignal.IsCancellationRequested) { }
}
- }
- }
- void SetTimer(TimeSpan interval)
- {
- _timer.Start(interval);
+ // Wait out the remainder of the batch fill time so that we don't overwhelm the server. With each
+ // successive failure the interval will increase. Needs special handling so that we don't need to
+ // make `fillBatch` cancellable (and thus fallible).
+ await Task.WhenAny(fillBatch, _waitForShutdownSignal).ConfigureAwait(false);
+ }
}
-
- ///
- /// Emit the provided log event to the sink. If the sink is being disposed or
- /// the app domain unloaded, then the event is ignored.
- ///
- /// Log event to emit.
- /// The event is null.
- ///
- /// The sink implements the contract that any events whose Emit() method has
- /// completed at the time of sink disposal will be flushed (or attempted to,
- /// depending on app domain state).
- ///
- public void Emit(LogEvent logEvent)
+ while (!_shutdownSignal.IsCancellationRequested);
+
+ // At this point:
+ // - The sink is being disposed
+ // - The queue has been completed
+ // - The queue may or may not be empty
+ // - The waiting batch may or may not be empty
+ // - The target sink may or may not be accepting events
+
+ // Try flushing the rest of the queue, but bail out on any failure. Shutdown time is unbounded, but it
+ // doesn't make sense to pick an arbitrary limit - a future version might add a new option to control this.
+ try
{
- if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
-
- if (_unloading)
- return;
-
- if (!_started)
+ while (_queue.Reader.TryPeek(out _))
{
- lock (_stateLock)
+ while (_currentBatch.Count < _batchSizeLimit &&
+ _queue.Reader.TryRead(out var next))
{
- if (_unloading) return;
- if (!_started)
- {
- _queue.TryEnqueue(logEvent);
- _started = true;
-
- if (_eagerlyEmitFirstEvent)
- {
- // Special handling to try to get the first event across as quickly
- // as possible to show we're alive!
- SetTimer(TimeSpan.Zero);
- }
- else
- {
- SetTimer(_status.NextInterval);
- }
+ _currentBatch.Enqueue(next);
+ }
- return;
- }
+ if (_currentBatch.Count != 0)
+ {
+ await _targetSink.EmitBatchAsync(_currentBatch).ConfigureAwait(false);
+ _currentBatch.Clear();
}
}
+ }
+ catch (Exception ex)
+ {
+ WriteToSelfLog("failed emitting a batch during shutdown; dropping remaining queued events", ex);
+ }
+ }
+
+ // Wait until `reader` has items to read. Returns `false` if the `timeout` task completes, or if the reader is cancelled.
+ async Task TryWaitToReadAsync(ChannelReader reader, Task timeout, CancellationToken cancellationToken)
+ {
+ var completed = await Task.WhenAny(timeout, reader.WaitToReadAsync(cancellationToken).AsTask()).ConfigureAwait(false);
- _queue.TryEnqueue(logEvent);
+ // Avoid unobserved task exceptions in the cancellation and failure cases. Note that we may not end up observing
+ // read task cancellation exceptions during shutdown, may be some room to improve.
+ if (completed is { Exception: not null, IsCanceled: false })
+ {
+ WriteToSelfLog($"could not read from queue: {completed.Exception}");
}
+
+ // `Task.IsCompletedSuccessfully` not available in .NET Standard 2.0/Framework.
+ return completed != timeout && completed is { IsCompleted: true, IsCanceled: false, IsFaulted: false };
+ }
+
+ ///
+ public void Dispose()
+ {
+ SignalShutdown();
- ///
- /// Determine whether a queued log event should be included in the batch. If
- /// an override returns false, the event will be dropped.
- ///
- /// An event to test for inclusion.
- /// True if the event should be included in the batch; otherwise, false.
- // ReSharper disable once UnusedParameter.Global
- protected virtual bool CanInclude(LogEvent logEvent)
+ try
+ {
+ _runLoop.Wait();
+ }
+ catch (Exception ex)
{
- return true;
+ // E.g. the task was canceled before ever being run, or internally failed and threw
+ // an unexpected exception.
+ WriteToSelfLog("caught exception during disposal", ex);
}
- ///
- /// Allows derived sinks to perform periodic work without requiring additional threads
- /// or timers (thus avoiding additional flush/shut-down complexity).
- ///
- /// Override either or ,
- /// not both.
- protected virtual void OnEmptyBatch()
+ (_targetSink as IDisposable)?.Dispose();
+ }
+
+#if FEATURE_ASYNCDISPOSABLE
+ ///
+ public async ValueTask DisposeAsync()
{
+ SignalShutdown();
+
+ try
+ {
+ await _runLoop.ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ // E.g. the task was canceled before ever being run, or internally failed and threw
+ // an unexpected exception.
+ WriteToSelfLog("caught exception during async disposal", ex);
+ }
+
+ if (_targetSink is IAsyncDisposable asyncDisposable)
+ await asyncDisposable.DisposeAsync().ConfigureAwait(false);
+ else
+ (_targetSink as IDisposable)?.Dispose();
}
+#endif
- ///
- /// Allows derived sinks to perform periodic work without requiring additional threads
- /// or timers (thus avoiding additional flush/shut-down complexity).
- ///
- /// Override either or ,
- /// not both.
-#pragma warning disable 1998
- protected virtual async Task OnEmptyBatchAsync()
-#pragma warning restore 1998
+ void SignalShutdown()
+ {
+ lock (_stateLock)
{
- // ReSharper disable once MethodHasAsyncOverload
- OnEmptyBatch();
+ if (!_shutdownSignal.IsCancellationRequested)
+ {
+ // Relies on synchronization via `_stateLock`: once the writer is completed, subsequent attempts to
+ // complete it will throw.
+ _queue.Writer.Complete();
+ _shutdownSignal.Cancel();
+ }
}
-
- Task IBatchedLogEventSink.EmitBatchAsync(IEnumerable batch) => EmitBatchAsync(batch);
- Task IBatchedLogEventSink.OnEmptyBatchAsync() => OnEmptyBatchAsync();
+ }
+
+ void WriteToSelfLog(string message, Exception? exception = null)
+ {
+ var ex = exception != null ? $"{Environment.NewLine}{exception}" : "";
+ SelfLog.WriteLine($"PeriodicBatchingSink ({_targetSink}): {message}{ex}");
}
}
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs
index 5acd457..c30a168 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs
@@ -12,36 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using System;
+namespace Serilog.Sinks.PeriodicBatching;
-namespace Serilog.Sinks.PeriodicBatching
+///
+/// Initialization options for .
+///
+public class PeriodicBatchingSinkOptions
{
///
- /// Initialization options for .
+ /// Eagerly emit a batch containing the first received event, regardless of
+ /// the target batch size or batching time. This helps with perceived "liveness"
+ /// when running/debugging applications interactively. The default is true.
///
- public class PeriodicBatchingSinkOptions
- {
- ///
- /// Eagerly emit a batch containing the first received event, regardless of
- /// the target batch size or batching time. This helps with perceived "liveness"
- /// when running/debugging applications interactively. The default is true.
- ///
- public bool EagerlyEmitFirstEvent { get; set; } = true;
+ public bool EagerlyEmitFirstEvent { get; set; } = true;
- ///
- /// The maximum number of events to include in a single batch. The default is 1000.
- ///
- public int BatchSizeLimit { get; set; } = 1000;
+ ///
+ /// The maximum number of events to include in a single batch. The default is 1000.
+ ///
+ public int BatchSizeLimit { get; set; } = 1000;
- ///
- /// The time to wait between checking for event batches. The default is two seconds.
- ///
- public TimeSpan Period { get; set; } = TimeSpan.FromSeconds(2);
+ ///
+ /// The maximum buffering delay between event batches. The default is two seconds.
+ ///
+ public TimeSpan Period { get; set; } = TimeSpan.FromSeconds(2);
- ///
- /// Maximum number of events to hold in the sink's internal queue, or null
- /// for an unbounded queue. The default is 100000.
- ///
- public int? QueueLimit { get; set; } = 100000;
- }
-}
+ ///
+ /// Maximum number of events to hold in the sink's internal queue, or null
+ /// for an unbounded queue. The default is 100000.
+ ///
+ public int? QueueLimit { get; set; } = 100000;
+}
\ No newline at end of file
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PortableTimer.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PortableTimer.cs
deleted file mode 100644
index ac6e44d..0000000
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PortableTimer.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright © Serilog Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using Serilog.Debugging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Serilog.Sinks.PeriodicBatching
-{
- class PortableTimer : IDisposable
- {
- readonly object _stateLock = new();
-
- readonly Func _onTick;
- readonly CancellationTokenSource _cancel = new();
-
-#if FEATURE_THREADING_TIMER
- readonly Timer _timer;
-#endif
-
- bool _running;
- bool _disposed;
-
- public PortableTimer(Func onTick)
- {
- _onTick = onTick ?? throw new ArgumentNullException(nameof(onTick));
-
-#if FEATURE_THREADING_TIMER
-#if FEATURE_EXECUTION_CONTEXT
- using (ExecutionContext.SuppressFlow())
-#endif
- _timer = new Timer(_ => OnTick(), null, Timeout.Infinite, Timeout.Infinite);
-#endif
- }
-
- public void Start(TimeSpan interval)
- {
- if (interval < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(interval));
-
- lock (_stateLock)
- {
- if (_disposed)
- throw new ObjectDisposedException(nameof(PortableTimer));
-
-#if FEATURE_THREADING_TIMER
- _timer.Change(interval, Timeout.InfiniteTimeSpan);
-#else
- Task.Delay(interval, _cancel.Token)
- .ContinueWith(
- _ => OnTick(),
- CancellationToken.None,
- TaskContinuationOptions.DenyChildAttach,
- TaskScheduler.Default);
-#endif
- }
- }
-
- async void OnTick()
- {
- try
- {
- lock (_stateLock)
- {
- if (_disposed)
- {
- return;
- }
-
- // There's a little bit of raciness here, but it's needed to support the
- // current API, which allows the tick handler to reenter and set the next interval.
-
- if (_running)
- {
- Monitor.Wait(_stateLock);
-
- if (_disposed)
- {
- return;
- }
- }
-
- _running = true;
- }
-
- if (!_cancel.Token.IsCancellationRequested)
- {
- await _onTick(_cancel.Token);
- }
- }
- catch (OperationCanceledException tcx)
- {
- SelfLog.WriteLine("The timer was canceled during invocation: {0}", tcx);
- }
- finally
- {
- lock (_stateLock)
- {
- _running = false;
- Monitor.PulseAll(_stateLock);
- }
- }
- }
-
- public void Dispose()
- {
- _cancel.Cancel();
-
- lock (_stateLock)
- {
- if (_disposed)
- {
- return;
- }
-
- while (_running)
- {
- Monitor.Wait(_stateLock);
- }
-
-#if FEATURE_THREADING_TIMER
- _timer.Dispose();
-#endif
-
- _disposed = true;
- }
- }
- }
-}
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/TaskUtil.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/TaskUtil.cs
deleted file mode 100644
index f9a2aad..0000000
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/TaskUtil.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright © Serilog Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Serilog.Sinks.PeriodicBatching;
-
-static class TaskUtil
-{
- public static void ResetSyncContextAndWait(Func taskFactory)
- {
- var prevContext = SynchronizationContext.Current;
- SynchronizationContext.SetSynchronizationContext(null);
- try
- {
- taskFactory().Wait();
- }
- finally
- {
- SynchronizationContext.SetSynchronizationContext(prevContext);
- }
- }
-}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/BoundedQueue_Enqueue_Benchmark.cs b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/BoundedQueue_Enqueue_Benchmark.cs
deleted file mode 100644
index b4cebb9..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/BoundedQueue_Enqueue_Benchmark.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using BenchmarkDotNet.Attributes;
-using Serilog.Events;
-using Serilog.Sinks.PeriodicBatching.PerformanceTests.Support;
-using Serilog.Tests.Support;
-using System;
-using System.Collections.Concurrent;
-using System.Threading.Tasks;
-
-namespace Serilog.Sinks.PeriodicBatching.PerformanceTests
-{
- public class BoundedQueue_Enqueue_Benchmark
- {
- const int NON_BOUNDED = -1;
-
- [Params(50, 100)]
- public int Items { get; set; }
-
- [Params(NON_BOUNDED, 50, 100)]
- public int Limit { get; set; }
-
- [Params(1, -1)]
- public int ConcurrencyLevel { get; set; }
-
- readonly LogEvent _logEvent = Some.LogEvent();
-
- Func> _concurrentQueueFactory;
- Func> _boundedConcurrentQueueFactory;
- Func> _blockingCollectionFactory;
- Func> _synchronizedQueueFactory;
-
- [Setup]
- public void Setup()
- {
- _concurrentQueueFactory = () => new ConcurrentQueue();
- _boundedConcurrentQueueFactory = Limit != NON_BOUNDED ? new Func>(() => new BoundedConcurrentQueue(Limit))
- : new Func>(() => new BoundedConcurrentQueue());
- _blockingCollectionFactory = Limit != NON_BOUNDED ? new Func>(() => new BlockingCollection(Limit))
- : new Func>(() => new BlockingCollection());
- _synchronizedQueueFactory = Limit != NON_BOUNDED ? new Func>(() => new SynchronizedQueue(Limit))
- : new Func>(() => new SynchronizedQueue());
- }
-
- [Benchmark(Baseline = true)]
- public void ConcurrentQueue()
- {
- var queue = _concurrentQueueFactory();
- EnqueueItems(evt => queue.Enqueue(evt));
- }
-
- [Benchmark]
- public void BoundedConcurrentQueue()
- {
- var queue = _boundedConcurrentQueueFactory();
- EnqueueItems(evt => queue.TryEnqueue(evt));
- }
-
- [Benchmark]
- public void BlockingCollection()
- {
- var queue = _blockingCollectionFactory();
- EnqueueItems(evt => queue.TryAdd(evt));
- }
-
- [Benchmark]
- public void SynchronizedQueue()
- {
- var queue = _synchronizedQueueFactory();
- EnqueueItems(evt => queue.TryEnqueue(evt));
- }
-
- void EnqueueItems(Action enqueueAction)
- {
- Parallel.For(0, Items, new ParallelOptions() { MaxDegreeOfParallelism = ConcurrencyLevel }, _ => enqueueAction(_logEvent));
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/BoundedQueue_Enqueue_Dequeue_Benchmark.cs b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/BoundedQueue_Enqueue_Dequeue_Benchmark.cs
deleted file mode 100644
index 6ab6b13..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/BoundedQueue_Enqueue_Dequeue_Benchmark.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using BenchmarkDotNet.Attributes;
-using Serilog.Events;
-using Serilog.Sinks.PeriodicBatching.PerformanceTests.Support;
-using Serilog.Tests.Support;
-using System;
-using System.Collections.Concurrent;
-using System.Threading.Tasks;
-
-namespace Serilog.Sinks.PeriodicBatching.PerformanceTests
-{
- public class BoundedQueue_Enqueue_Dequeue_Benchmark
- {
- const int NON_BOUNDED = -1;
-
- [Params(50, 100)]
- public int Items { get; set; }
-
- [Params(NON_BOUNDED, 50, 100)]
- public int Limit { get; set; }
-
- readonly LogEvent _logEvent = Some.LogEvent();
-
- Func> _concurrentQueueFactory;
- Func> _boundedConcurrentQueueFactory;
- Func> _blockingCollectionFactory;
- Func> _synchronizedQueueFactory;
-
- [Setup]
- public void Setup()
- {
- _concurrentQueueFactory = () => new ConcurrentQueue();
- _boundedConcurrentQueueFactory = Limit != NON_BOUNDED ? new Func>(() => new BoundedConcurrentQueue(Limit))
- : new Func>(() => new BoundedConcurrentQueue());
- _blockingCollectionFactory = Limit != NON_BOUNDED ? new Func>(() => new BlockingCollection(Limit))
- : new Func>(() => new BlockingCollection());
- _synchronizedQueueFactory = Limit != NON_BOUNDED ? new Func>(() => new SynchronizedQueue(Limit))
- : new Func>(() => new SynchronizedQueue());
- }
-
- [Benchmark(Baseline = true)]
- public void ConcurrentQueue()
- {
- var queue = _concurrentQueueFactory();
- EnqueueDequeueItems(evt => queue.Enqueue(evt), evt => queue.TryDequeue(out evt));
- }
-
- [Benchmark]
- public void BoundedConcurrentQueue()
- {
- var queue = _boundedConcurrentQueueFactory();
- EnqueueDequeueItems(evt => queue.TryEnqueue(evt), evt => queue.TryDequeue(out evt));
- }
-
- [Benchmark]
- public void BlockingCollection()
- {
- var queue = _blockingCollectionFactory();
- EnqueueDequeueItems(evt => queue.TryAdd(evt), evt => queue.TryTake(out evt));
- }
-
- [Benchmark]
- public void SynchronizedQueue()
- {
- var queue = _synchronizedQueueFactory();
- EnqueueDequeueItems(evt => queue.TryEnqueue(evt), evt => queue.TryDequeue(out evt));
- }
-
- void EnqueueDequeueItems(Action enqueueAction, Action dequeueAction)
- {
- Parallel.Invoke(
- () => Parallel.For(0, Items, _ => enqueueAction(_logEvent)),
- () => { for (var i = 0; i < Items; i++) dequeueAction(_logEvent); });
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Runner.cs b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Runner.cs
deleted file mode 100644
index d25375b..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Runner.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using BenchmarkDotNet.Running;
-using Xunit;
-
-namespace Serilog.Sinks.PeriodicBatching.PerformanceTests
-{
- public class Runner
- {
- [Fact]
- public void BoundedQueue_Enqueue()
- {
- BenchmarkRunner.Run();
- }
-
- [Fact]
- public void BoundedQueue_Enqueue_Dequeue()
- {
- BenchmarkRunner.Run();
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Serilog.Sinks.PeriodicBatching.PerformanceTests.csproj b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Serilog.Sinks.PeriodicBatching.PerformanceTests.csproj
deleted file mode 100644
index 57704d7..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Serilog.Sinks.PeriodicBatching.PerformanceTests.csproj
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
- net452;netcoreapp1.1
- ../../assets/Serilog.snk
- true
- true
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Support/Some.cs b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Support/Some.cs
deleted file mode 100644
index 0df2968..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Support/Some.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using Serilog.Events;
-using Serilog.Parsing;
-
-namespace Serilog.Tests.Support
-{
- static class Some
- {
- static int Counter;
-
- public static int Int()
- {
- return Interlocked.Increment(ref Counter);
- }
-
- public static decimal Decimal()
- {
- return Int() + 0.123m;
- }
-
- public static string String(string tag = null)
- {
- return (tag ?? "") + "__" + Int();
- }
-
- public static TimeSpan TimeSpan()
- {
- return System.TimeSpan.FromMinutes(Int());
- }
-
- public static DateTime Instant()
- {
- return new DateTime(2012, 10, 28) + TimeSpan();
- }
-
- public static DateTimeOffset OffsetInstant()
- {
- return new DateTimeOffset(Instant());
- }
-
- public static LogEvent LogEvent(DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information)
- {
- return new LogEvent(timestamp ?? OffsetInstant(), level,
- null, MessageTemplate(), Enumerable.Empty());
- }
-
- public static LogEvent InformationEvent(DateTimeOffset? timestamp = null)
- {
- return LogEvent(timestamp, LogEventLevel.Information);
- }
-
- public static LogEvent DebugEvent(DateTimeOffset? timestamp = null)
- {
- return LogEvent(timestamp, LogEventLevel.Debug);
- }
-
- public static LogEventProperty LogEventProperty()
- {
- return new LogEventProperty(String(), new ScalarValue(Int()));
- }
-
- public static string NonexistentTempFilePath()
- {
- return Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".txt");
- }
-
- public static string TempFilePath()
- {
- return Path.GetTempFileName();
- }
-
- public static string TempFolderPath()
- {
- var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- Directory.CreateDirectory(dir);
- return dir;
- }
-
- public static MessageTemplate MessageTemplate()
- {
- return new MessageTemplateParser().Parse(String());
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Support/SynchronizedQueue.cs b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Support/SynchronizedQueue.cs
deleted file mode 100644
index 049fa63..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/Support/SynchronizedQueue.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System.Collections.Generic;
-
-namespace Serilog.Sinks.PeriodicBatching.PerformanceTests.Support
-{
- class SynchronizedQueue : Queue
- {
- const int NON_BOUNDED = -1;
-
- readonly int _queueLimit;
-
- public SynchronizedQueue()
- {
- _queueLimit = NON_BOUNDED;
- }
-
- public SynchronizedQueue(int queueLimit)
- {
- _queueLimit = queueLimit;
- }
-
- public bool TryDequeue(out T item)
- {
- item = default(T);
-
- lock (this)
- {
- if (base.Count > 0)
- {
- item = base.Dequeue();
- return true;
- }
-
- return false;
- }
- }
-
- public bool TryEnqueue(T item)
- {
- lock (this)
- {
- if (base.Count < _queueLimit || _queueLimit == NON_BOUNDED)
- {
- base.Enqueue(item);
- return true;
- }
-
- return false;
- }
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/net452/BoundedQueue_Enqueue_Benchmark-report-github.md b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/net452/BoundedQueue_Enqueue_Benchmark-report-github.md
deleted file mode 100644
index 73113a3..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/net452/BoundedQueue_Enqueue_Benchmark-report-github.md
+++ /dev/null
@@ -1,60 +0,0 @@
-``` ini
-
-BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0
-Processor=Intel(R) Core(TM) i7-4790 CPU 3.60GHz, ProcessorCount=8
-Frequency=3507499 Hz, Resolution=285.1034 ns, Timer=TSC
- [Host] : Clr 4.0.30319.42000, 32bit LegacyJIT-v4.6.1586.0
- DefaultJob : Clr 4.0.30319.42000, 32bit LegacyJIT-v4.6.1586.0
-
-
-```
- | Method | Items | Limit | ConcurrencyLevel | Mean | StdErr | StdDev | Scaled | Scaled-StdDev |
- |----------------------- |------ |------ |----------------- |-------------- |---------- |---------- |------- |-------------- |
- | **ConcurrentQueue** | **50** | **-1** | **-1** | **7.2804 us** | **0.0103 us** | **0.0401 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | -1 | -1 | 7.2908 us | 0.0388 us | 0.1503 us | 1.00 | 0.02 |
- | BlockingCollection | 50 | -1 | -1 | 17.7474 us | 0.0848 us | 0.3059 us | 2.44 | 0.04 |
- | SynchronizedQueue | 50 | -1 | -1 | 12.1052 us | 0.0216 us | 0.0837 us | 1.66 | 0.01 |
- | **ConcurrentQueue** | **50** | **-1** | **1** | **3.4143 us** | **0.0338 us** | **0.1551 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | -1 | 1 | 3.5365 us | 0.0352 us | 0.1409 us | 1.04 | 0.06 |
- | BlockingCollection | 50 | -1 | 1 | 6.8568 us | 0.0684 us | 0.3683 us | 2.01 | 0.14 |
- | SynchronizedQueue | 50 | -1 | 1 | 4.4259 us | 0.0376 us | 0.1455 us | 1.30 | 0.07 |
- | **ConcurrentQueue** | **50** | **50** | **-1** | **7.2713 us** | **0.0079 us** | **0.0297 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 50 | -1 | 8.4583 us | 0.0461 us | 0.1787 us | 1.16 | 0.02 |
- | BlockingCollection | 50 | 50 | -1 | 23.0957 us | 0.1349 us | 0.5224 us | 3.18 | 0.07 |
- | SynchronizedQueue | 50 | 50 | -1 | 12.0785 us | 0.0264 us | 0.1023 us | 1.66 | 0.02 |
- | **ConcurrentQueue** | **50** | **50** | **1** | **3.3591 us** | **0.0283 us** | **0.1019 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 50 | 1 | 3.8662 us | 0.0378 us | 0.1730 us | 1.15 | 0.06 |
- | BlockingCollection | 50 | 50 | 1 | 9.2685 us | 0.0939 us | 0.8237 us | 2.76 | 0.26 |
- | SynchronizedQueue | 50 | 50 | 1 | 4.3973 us | 0.0456 us | 0.3288 us | 1.31 | 0.10 |
- | **ConcurrentQueue** | **50** | **100** | **-1** | **7.2897 us** | **0.0112 us** | **0.0434 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 100 | -1 | 8.4600 us | 0.0271 us | 0.1049 us | 1.16 | 0.02 |
- | BlockingCollection | 50 | 100 | -1 | 22.9237 us | 0.1219 us | 0.4723 us | 3.14 | 0.07 |
- | SynchronizedQueue | 50 | 100 | -1 | 12.0344 us | 0.0149 us | 0.0578 us | 1.65 | 0.01 |
- | **ConcurrentQueue** | **50** | **100** | **1** | **3.4225 us** | **0.0333 us** | **0.1596 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 100 | 1 | 4.0642 us | 0.0207 us | 0.0716 us | 1.19 | 0.06 |
- | BlockingCollection | 50 | 100 | 1 | 9.2182 us | 0.0922 us | 0.8194 us | 2.70 | 0.27 |
- | SynchronizedQueue | 50 | 100 | 1 | 4.3935 us | 0.0434 us | 0.2713 us | 1.29 | 0.10 |
- | **ConcurrentQueue** | **100** | **-1** | **-1** | **10.4820 us** | **0.0080 us** | **0.0301 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | -1 | -1 | 11.2203 us | 0.0277 us | 0.1075 us | 1.07 | 0.01 |
- | BlockingCollection | 100 | -1 | -1 | 36.4722 us | 0.2981 us | 1.1544 us | 3.48 | 0.11 |
- | SynchronizedQueue | 100 | -1 | -1 | 22.3040 us | 0.1371 us | 0.5130 us | 2.13 | 0.05 |
- | **ConcurrentQueue** | **100** | **-1** | **1** | **4.7591 us** | **0.0396 us** | **0.1534 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | -1 | 1 | 5.0724 us | 0.0504 us | 0.2715 us | 1.07 | 0.07 |
- | BlockingCollection | 100 | -1 | 1 | 10.6618 us | 0.1146 us | 0.7856 us | 2.24 | 0.18 |
- | SynchronizedQueue | 100 | -1 | 1 | 6.5492 us | 0.0650 us | 0.5438 us | 1.38 | 0.12 |
- | **ConcurrentQueue** | **100** | **50** | **-1** | **10.5168 us** | **0.0088 us** | **0.0329 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 50 | -1 | 11.8475 us | 0.0321 us | 0.1201 us | 1.13 | 0.01 |
- | BlockingCollection | 100 | 50 | -1 | 368.4878 us | 0.3846 us | 1.4392 us | 35.04 | 0.17 |
- | SynchronizedQueue | 100 | 50 | -1 | 19.3906 us | 0.1377 us | 0.5333 us | 1.84 | 0.05 |
- | **ConcurrentQueue** | **100** | **50** | **1** | **5.8222 us** | **0.0265 us** | **0.1243 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 50 | 1 | 5.0835 us | 0.0500 us | 0.2447 us | 0.87 | 0.05 |
- | BlockingCollection | 100 | 50 | 1 | 1,418.6158 us | 1.9614 us | 7.5965 us | 243.77 | 5.69 |
- | SynchronizedQueue | 100 | 50 | 1 | 6.1161 us | 0.0601 us | 0.4073 us | 1.05 | 0.07 |
- | **ConcurrentQueue** | **100** | **100** | **-1** | **10.4971 us** | **0.0164 us** | **0.0633 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 100 | -1 | 12.6199 us | 0.0405 us | 0.1569 us | 1.20 | 0.02 |
- | BlockingCollection | 100 | 100 | -1 | 51.6912 us | 0.5041 us | 2.5203 us | 4.92 | 0.24 |
- | SynchronizedQueue | 100 | 100 | -1 | 23.4763 us | 0.1860 us | 0.7204 us | 2.24 | 0.07 |
- | **ConcurrentQueue** | **100** | **100** | **1** | **4.8530 us** | **0.0388 us** | **0.1503 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 100 | 1 | 5.8461 us | 0.0580 us | 0.4141 us | 1.21 | 0.09 |
- | BlockingCollection | 100 | 100 | 1 | 14.9473 us | 0.1493 us | 1.3015 us | 3.08 | 0.28 |
- | SynchronizedQueue | 100 | 100 | 1 | 6.5940 us | 0.0655 us | 0.5360 us | 1.36 | 0.12 |
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/net452/BoundedQueue_Enqueue_Dequeue_Benchmark-report-github.md b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/net452/BoundedQueue_Enqueue_Dequeue_Benchmark-report-github.md
deleted file mode 100644
index 46ec454..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/net452/BoundedQueue_Enqueue_Dequeue_Benchmark-report-github.md
+++ /dev/null
@@ -1,36 +0,0 @@
-``` ini
-
-BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0
-Processor=Intel(R) Core(TM) i7-4790 CPU 3.60GHz, ProcessorCount=8
-Frequency=3507499 Hz, Resolution=285.1034 ns, Timer=TSC
- [Host] : Clr 4.0.30319.42000, 32bit LegacyJIT-v4.6.1586.0
- DefaultJob : Clr 4.0.30319.42000, 32bit LegacyJIT-v4.6.1586.0
-
-
-```
- | Method | Items | Limit | Mean | StdErr | StdDev | Scaled | Scaled-StdDev |
- |----------------------- |------ |------ |------------ |---------- |---------- |------- |-------------- |
- | **ConcurrentQueue** | **50** | **-1** | **10.3018 us** | **0.0261 us** | **0.0940 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | -1 | 11.0476 us | 0.0208 us | 0.0804 us | 1.07 | 0.01 |
- | BlockingCollection | 50 | -1 | 37.3304 us | 0.1895 us | 0.7091 us | 3.62 | 0.07 |
- | SynchronizedQueue | 50 | -1 | 20.8889 us | 0.0861 us | 0.3223 us | 2.03 | 0.03 |
- | **ConcurrentQueue** | **50** | **50** | **10.3639 us** | **0.0167 us** | **0.0648 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 50 | 12.5340 us | 0.0182 us | 0.0706 us | 1.21 | 0.01 |
- | BlockingCollection | 50 | 50 | 52.3100 us | 0.5042 us | 1.8865 us | 5.05 | 0.18 |
- | SynchronizedQueue | 50 | 50 | 20.8874 us | 0.1339 us | 0.5186 us | 2.02 | 0.05 |
- | **ConcurrentQueue** | **50** | **100** | **10.2835 us** | **0.0262 us** | **0.0981 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 100 | 12.5809 us | 0.0318 us | 0.1230 us | 1.22 | 0.02 |
- | BlockingCollection | 50 | 100 | 50.8533 us | 0.4826 us | 1.9898 us | 4.95 | 0.19 |
- | SynchronizedQueue | 50 | 100 | 20.5994 us | 0.1159 us | 0.4338 us | 2.00 | 0.04 |
- | **ConcurrentQueue** | **100** | **-1** | **14.7901 us** | **0.0334 us** | **0.1295 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | -1 | 15.3619 us | 0.0328 us | 0.1270 us | 1.04 | 0.01 |
- | BlockingCollection | 100 | -1 | 67.4710 us | 0.3917 us | 1.4122 us | 4.56 | 0.10 |
- | SynchronizedQueue | 100 | -1 | 41.1039 us | 0.4912 us | 2.0254 us | 2.78 | 0.13 |
- | **ConcurrentQueue** | **100** | **50** | **14.8436 us** | **0.0276 us** | **0.0956 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 50 | 17.3593 us | 0.0352 us | 0.1364 us | 1.17 | 0.01 |
- | BlockingCollection | 100 | 50 | 173.0738 us | 1.0031 us | 3.7533 us | 11.66 | 0.25 |
- | SynchronizedQueue | 100 | 50 | 36.4733 us | 0.2613 us | 1.0121 us | 2.46 | 0.07 |
- | **ConcurrentQueue** | **100** | **100** | **14.8464 us** | **0.0231 us** | **0.0894 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 100 | 17.4290 us | 0.0391 us | 0.1516 us | 1.17 | 0.01 |
- | BlockingCollection | 100 | 100 | 91.5932 us | 0.9253 us | 4.3401 us | 6.17 | 0.29 |
- | SynchronizedQueue | 100 | 100 | 40.4670 us | 0.3657 us | 1.3186 us | 2.73 | 0.09 |
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/netcoreapp1.1/BoundedQueue_Enqueue_Benchmark-report-github.md b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/netcoreapp1.1/BoundedQueue_Enqueue_Benchmark-report-github.md
deleted file mode 100644
index d82acc4..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/netcoreapp1.1/BoundedQueue_Enqueue_Benchmark-report-github.md
+++ /dev/null
@@ -1,61 +0,0 @@
-``` ini
-
-BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows 10.0.14393
-Processor=Intel(R) Core(TM) i7-4790 CPU 3.60GHz, ProcessorCount=8
-Frequency=3507499 Hz, Resolution=285.1034 ns, Timer=TSC
-dotnet cli version=1.0.0
- [Host] : .NET Core 4.6.25009.03, 64bit RyuJIT
- DefaultJob : .NET Core 4.6.25009.03, 64bit RyuJIT
-
-
-```
- | Method | Items | Limit | ConcurrencyLevel | Mean | StdErr | StdDev | Scaled | Scaled-StdDev |
- |----------------------- |------ |------ |----------------- |----------- |---------- |---------- |------- |-------------- |
- | **ConcurrentQueue** | **50** | **-1** | **-1** | **5.1811 us** | **0.0061 us** | **0.0237 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | -1 | -1 | 5.5466 us | 0.0111 us | 0.0431 us | 1.07 | 0.01 |
- | BlockingCollection | 50 | -1 | -1 | 14.6056 us | 0.0977 us | 0.3783 us | 2.82 | 0.07 |
- | SynchronizedQueue | 50 | -1 | -1 | 7.6025 us | 0.0130 us | 0.0505 us | 1.47 | 0.01 |
- | **ConcurrentQueue** | **50** | **-1** | **1** | **1.9633 us** | **0.0018 us** | **0.0069 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | -1 | 1 | 2.1595 us | 0.0067 us | 0.0232 us | 1.10 | 0.01 |
- | BlockingCollection | 50 | -1 | 1 | 4.4581 us | 0.0030 us | 0.0111 us | 2.27 | 0.01 |
- | SynchronizedQueue | 50 | -1 | 1 | 2.4642 us | 0.0039 us | 0.0151 us | 1.26 | 0.01 |
- | **ConcurrentQueue** | **50** | **50** | **-1** | **5.1484 us** | **0.0080 us** | **0.0312 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 50 | -1 | 6.3047 us | 0.0042 us | 0.0153 us | 1.22 | 0.01 |
- | BlockingCollection | 50 | 50 | -1 | 21.8181 us | 0.2179 us | 1.1734 us | 4.24 | 0.23 |
- | SynchronizedQueue | 50 | 50 | -1 | 7.5549 us | 0.0140 us | 0.0542 us | 1.47 | 0.01 |
- | **ConcurrentQueue** | **50** | **50** | **1** | **1.9670 us** | **0.0023 us** | **0.0090 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 50 | 1 | 2.4802 us | 0.0022 us | 0.0083 us | 1.26 | 0.01 |
- | BlockingCollection | 50 | 50 | 1 | 6.9471 us | 0.0075 us | 0.0281 us | 3.53 | 0.02 |
- | SynchronizedQueue | 50 | 50 | 1 | 2.4790 us | 0.0012 us | 0.0048 us | 1.26 | 0.01 |
- | **ConcurrentQueue** | **50** | **100** | **-1** | **5.5230 us** | **0.0074 us** | **0.0286 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 100 | -1 | 6.3623 us | 0.0122 us | 0.0456 us | 1.15 | 0.01 |
- | BlockingCollection | 50 | 100 | -1 | 21.1418 us | 0.1764 us | 0.6600 us | 3.83 | 0.12 |
- | SynchronizedQueue | 50 | 100 | -1 | 7.5030 us | 0.0113 us | 0.0422 us | 1.36 | 0.01 |
- | **ConcurrentQueue** | **50** | **100** | **1** | **1.9761 us** | **0.0030 us** | **0.0118 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 100 | 1 | 2.4867 us | 0.0015 us | 0.0058 us | 1.26 | 0.01 |
- | BlockingCollection | 50 | 100 | 1 | 6.9420 us | 0.0099 us | 0.0385 us | 3.51 | 0.03 |
- | SynchronizedQueue | 50 | 100 | 1 | 2.4812 us | 0.0021 us | 0.0083 us | 1.26 | 0.01 |
- | **ConcurrentQueue** | **100** | **-1** | **-1** | **7.6981 us** | **0.0179 us** | **0.0692 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | -1 | -1 | 8.0702 us | 0.0197 us | 0.0763 us | 1.05 | 0.01 |
- | BlockingCollection | 100 | -1 | -1 | 32.4460 us | 0.4472 us | 1.6731 us | 4.22 | 0.21 |
- | SynchronizedQueue | 100 | -1 | -1 | 12.7754 us | 0.0340 us | 0.1272 us | 1.66 | 0.02 |
- | **ConcurrentQueue** | **100** | **-1** | **1** | **3.0726 us** | **0.0040 us** | **0.0154 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | -1 | 1 | 3.4069 us | 0.0024 us | 0.0095 us | 1.11 | 0.01 |
- | BlockingCollection | 100 | -1 | 1 | 7.7433 us | 0.0116 us | 0.0435 us | 2.52 | 0.02 |
- | SynchronizedQueue | 100 | -1 | 1 | 3.7620 us | 0.0022 us | 0.0079 us | 1.22 | 0.01 |
- | **ConcurrentQueue** | **100** | **50** | **-1** | **7.8410 us** | **0.0306 us** | **0.1184 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 50 | -1 | 8.4654 us | 0.0106 us | 0.0395 us | 1.08 | 0.02 |
- | BlockingCollection | 100 | 50 | -1 | 28.9868 us | 0.2876 us | 1.0761 us | 3.70 | 0.14 |
- | SynchronizedQueue | 100 | 50 | -1 | 11.0092 us | 0.0305 us | 0.1183 us | 1.40 | 0.03 |
- | **ConcurrentQueue** | **100** | **50** | **1** | **3.0622 us** | **0.0029 us** | **0.0103 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 50 | 1 | 3.3265 us | 0.0031 us | 0.0122 us | 1.09 | 0.01 |
- | BlockingCollection | 100 | 50 | 1 | 8.1403 us | 0.0100 us | 0.0386 us | 2.66 | 0.01 |
- | SynchronizedQueue | 100 | 50 | 1 | 3.4894 us | 0.0032 us | 0.0126 us | 1.14 | 0.01 |
- | **ConcurrentQueue** | **100** | **100** | **-1** | **7.6139 us** | **0.0162 us** | **0.0607 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 100 | -1 | 9.5550 us | 0.0392 us | 0.1517 us | 1.26 | 0.02 |
- | BlockingCollection | 100 | 100 | -1 | 47.2014 us | 0.4680 us | 2.3402 us | 6.20 | 0.30 |
- | SynchronizedQueue | 100 | 100 | -1 | 12.6909 us | 0.0638 us | 0.2471 us | 1.67 | 0.03 |
- | **ConcurrentQueue** | **100** | **100** | **1** | **3.0812 us** | **0.0045 us** | **0.0174 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 100 | 1 | 4.0404 us | 0.0046 us | 0.0179 us | 1.31 | 0.01 |
- | BlockingCollection | 100 | 100 | 1 | 12.7325 us | 0.0157 us | 0.0607 us | 4.13 | 0.03 |
- | SynchronizedQueue | 100 | 100 | 1 | 3.7607 us | 0.0032 us | 0.0119 us | 1.22 | 0.01 |
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/netcoreapp1.1/BoundedQueue_Enqueue_Dequeue_Benchmark-report-github.md b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/netcoreapp1.1/BoundedQueue_Enqueue_Dequeue_Benchmark-report-github.md
deleted file mode 100644
index bbedcad..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/results/netcoreapp1.1/BoundedQueue_Enqueue_Dequeue_Benchmark-report-github.md
+++ /dev/null
@@ -1,37 +0,0 @@
-``` ini
-
-BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows 10.0.14393
-Processor=Intel(R) Core(TM) i7-4790 CPU 3.60GHz, ProcessorCount=8
-Frequency=3507499 Hz, Resolution=285.1034 ns, Timer=TSC
-dotnet cli version=1.0.0
- [Host] : .NET Core 4.6.25009.03, 64bit RyuJIT
- DefaultJob : .NET Core 4.6.25009.03, 64bit RyuJIT
-
-
-```
- | Method | Items | Limit | Mean | StdDev | Scaled | Scaled-StdDev |
- |----------------------- |------ |------ |----------- |---------- |------- |-------------- |
- | **ConcurrentQueue** | **50** | **-1** | **7.2422 us** | **0.0720 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | -1 | 7.6356 us | 0.0919 us | 1.05 | 0.02 |
- | BlockingCollection | 50 | -1 | 25.4929 us | 0.8222 us | 3.52 | 0.11 |
- | SynchronizedQueue | 50 | -1 | 12.8276 us | 0.1913 us | 1.77 | 0.03 |
- | **ConcurrentQueue** | **50** | **50** | **7.2297 us** | **0.0630 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 50 | 8.7806 us | 0.0856 us | 1.21 | 0.02 |
- | BlockingCollection | 50 | 50 | 35.1334 us | 0.7982 us | 4.86 | 0.11 |
- | SynchronizedQueue | 50 | 50 | 12.8640 us | 0.2109 us | 1.78 | 0.03 |
- | **ConcurrentQueue** | **50** | **100** | **7.3600 us** | **0.1077 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 50 | 100 | 8.8626 us | 0.0979 us | 1.20 | 0.02 |
- | BlockingCollection | 50 | 100 | 36.0215 us | 0.8452 us | 4.90 | 0.13 |
- | SynchronizedQueue | 50 | 100 | 12.9318 us | 0.1471 us | 1.76 | 0.03 |
- | **ConcurrentQueue** | **100** | **-1** | **10.6257 us** | **0.1468 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | -1 | 11.1374 us | 0.1332 us | 1.05 | 0.02 |
- | BlockingCollection | 100 | -1 | 49.6138 us | 1.3730 us | 4.67 | 0.14 |
- | SynchronizedQueue | 100 | -1 | 23.9550 us | 0.6100 us | 2.25 | 0.06 |
- | **ConcurrentQueue** | **100** | **50** | **10.6249 us** | **0.0920 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 50 | 12.9409 us | 0.1868 us | 1.22 | 0.02 |
- | BlockingCollection | 100 | 50 | 55.0996 us | 2.3733 us | 5.19 | 0.22 |
- | SynchronizedQueue | 100 | 50 | 22.3050 us | 0.8641 us | 2.10 | 0.08 |
- | **ConcurrentQueue** | **100** | **100** | **10.6000 us** | **0.1713 us** | **1.00** | **0.00** |
- | BoundedConcurrentQueue | 100 | 100 | 13.2870 us | 0.2239 us | 1.25 | 0.03 |
- | BlockingCollection | 100 | 100 | 59.8952 us | 2.8346 us | 5.65 | 0.28 |
- | SynchronizedQueue | 100 | 100 | 23.5773 us | 0.6238 us | 2.22 | 0.07 |
diff --git a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/xunit.runner.json b/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/xunit.runner.json
deleted file mode 100644
index 07120b4..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.PerformanceTests/xunit.runner.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "shadowCopy": false
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/BackwardsCompatibilityTests.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/BackwardsCompatibilityTests.cs
deleted file mode 100644
index a1d53bc..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/BackwardsCompatibilityTests.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-#if FEATURE_ASYNCDISPOSABLE
-
-using System.Threading.Tasks;
-using Serilog.Sinks.PeriodicBatching.Tests.Support;
-using Xunit;
-
-namespace Serilog.Sinks.PeriodicBatching.Tests;
-
-public class BackwardsCompatibilityTests
-{
- [Fact]
- public async Task LegacySinksAreDisposedWhenLoggerIsDisposedAsync()
- {
- var sink = new LegacyDisposeTrackingSink();
- var logger = new LoggerConfiguration().WriteTo.Sink(sink).CreateLogger();
- await logger.DisposeAsync();
- Assert.True(sink.IsDisposed);
- }
-}
-
-#endif
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/BatchedConnectionStatusTests.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/BatchedConnectionStatusTests.cs
deleted file mode 100644
index f94375c..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/BatchedConnectionStatusTests.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using System;
-using System.Globalization;
-using Xunit;
-
-namespace Serilog.Sinks.PeriodicBatching.Tests
-{
- public class BatchedConnectionStatusTests
- {
- readonly TimeSpan _defaultPeriod = TimeSpan.FromSeconds(2);
-
- [Fact]
- public void WhenNoFailuresHaveOccurredTheRegularIntervalIsUsed()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- Assert.Equal(_defaultPeriod, bcs.NextInterval);
- }
-
- [Fact]
- public void WhenOneFailureHasOccurredTheRegularIntervalIsUsed()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- bcs.MarkFailure();
- Assert.Equal(_defaultPeriod, bcs.NextInterval);
- }
-
- [Fact]
- public void WhenTwoFailuresHaveOccurredTheIntervalBacksOff()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- bcs.MarkFailure();
- bcs.MarkFailure();
- Assert.Equal(TimeSpan.FromSeconds(10), bcs.NextInterval);
- }
-
- [Fact]
- public void WhenABatchSucceedsTheStatusResets()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- bcs.MarkFailure();
- bcs.MarkFailure();
- bcs.MarkSuccess();
- Assert.Equal(_defaultPeriod, bcs.NextInterval);
- }
-
- [Fact]
- public void WhenThreeFailuresHaveOccurredTheIntervalBacksOff()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- bcs.MarkFailure();
- bcs.MarkFailure();
- bcs.MarkFailure();
- Assert.Equal(TimeSpan.FromSeconds(20), bcs.NextInterval);
- Assert.False(bcs.ShouldDropBatch);
- }
-
- [Fact]
- public void When8FailuresHaveOccurredTheIntervalBacksOffAndBatchIsDropped()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- for (var i = 0; i < 8; ++i)
- {
- Assert.False(bcs.ShouldDropBatch);
- bcs.MarkFailure();
- }
- Assert.Equal(TimeSpan.FromMinutes(10), bcs.NextInterval);
- Assert.True(bcs.ShouldDropBatch);
- Assert.False(bcs.ShouldDropQueue);
- }
-
- [Fact]
- public void When10FailuresHaveOccurredTheQueueIsDropped()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- for (var i = 0; i < 10; ++i)
- {
- Assert.False(bcs.ShouldDropQueue);
- bcs.MarkFailure();
- }
- Assert.True(bcs.ShouldDropQueue);
- }
-
- [Fact]
- public void AtTheDefaultIntervalRetriesFor10MinutesBeforeDroppingBatch()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- var cumulative = TimeSpan.Zero;
- do
- {
- bcs.MarkFailure();
-
- if (!bcs.ShouldDropBatch)
- cumulative += bcs.NextInterval;
- } while (!bcs.ShouldDropBatch);
-
- Assert.False(bcs.ShouldDropQueue);
- Assert.Equal(TimeSpan.Parse("00:10:32", CultureInfo.InvariantCulture), cumulative);
- }
-
- [Fact]
- public void AtTheDefaultIntervalRetriesFor30MinutesBeforeDroppingQueue()
- {
- var bcs = new BatchedConnectionStatus(_defaultPeriod);
- var cumulative = TimeSpan.Zero;
- do
- {
- bcs.MarkFailure();
-
- if (!bcs.ShouldDropQueue)
- cumulative += bcs.NextInterval;
- } while (!bcs.ShouldDropQueue);
-
- Assert.Equal(TimeSpan.Parse("00:30:32", CultureInfo.InvariantCulture), cumulative);
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/BoundedConcurrentQueueTests.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/BoundedConcurrentQueueTests.cs
deleted file mode 100644
index d65f193..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/BoundedConcurrentQueueTests.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-using Xunit;
-
-namespace Serilog.Sinks.PeriodicBatching.Tests
-{
- public class BoundedConcurrentQueueTests
- {
- [Fact]
- public void WhenBoundedShouldNotExceedLimit()
- {
- const int limit = 100;
- var queue = new BoundedConcurrentQueue(limit);
-
- for (var i = 0; i < limit * 2; i++)
- {
- queue.TryEnqueue(i);
- }
-
- Assert.Equal(limit, queue.Count);
- }
-
- [Fact]
- public void WhenInvalidLimitWillThrow()
- {
- Assert.Throws(() => new BoundedConcurrentQueue(-42));
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/FailureAwareBatchSchedulerTests.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/FailureAwareBatchSchedulerTests.cs
new file mode 100644
index 0000000..2ac40e8
--- /dev/null
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/FailureAwareBatchSchedulerTests.cs
@@ -0,0 +1,105 @@
+using System.Globalization;
+using Xunit;
+
+namespace Serilog.Sinks.PeriodicBatching.Tests;
+
+public class FailureAwareBatchSchedulerTests
+{
+ static TimeSpan Period => TimeSpan.FromSeconds(2);
+ FailureAwareBatchScheduler Scheduler { get; } = new(Period);
+
+ [Fact]
+ public void WhenNoFailuresHaveOccurredTheInitialIntervalIsUsed()
+ {
+ Assert.Equal(Period, Scheduler.NextInterval);
+ }
+
+ [Fact]
+ public void WhenOneFailureHasOccurredTheInitialIntervalIsUsed()
+ {
+ Scheduler.MarkFailure();
+ Assert.Equal(Period, Scheduler.NextInterval);
+ }
+
+ [Fact]
+ public void WhenTwoFailuresHaveOccurredTheIntervalBacksOff()
+ {
+ Scheduler.MarkFailure();
+ Scheduler.MarkFailure();
+ Assert.Equal(TimeSpan.FromSeconds(10), Scheduler.NextInterval);
+ }
+
+ [Fact]
+ public void WhenABatchSucceedsTheStatusResets()
+ {
+ Scheduler.MarkFailure();
+ Scheduler.MarkFailure();
+ Scheduler.MarkSuccess();
+ Assert.Equal(Period, Scheduler.NextInterval);
+ }
+
+ [Fact]
+ public void WhenThreeFailuresHaveOccurredTheIntervalBacksOff()
+ {
+ Scheduler.MarkFailure();
+ Scheduler.MarkFailure();
+ Scheduler.MarkFailure();
+ Assert.Equal(TimeSpan.FromSeconds(20), Scheduler.NextInterval);
+ Assert.False(Scheduler.ShouldDropBatch);
+ }
+
+ [Fact]
+ public void WhenEightFailuresHaveOccurredTheIntervalBacksOffAndBatchIsDropped()
+ {
+ for (var i = 0; i < 8; ++i)
+ {
+ Assert.False(Scheduler.ShouldDropBatch);
+ Scheduler.MarkFailure();
+ }
+ Assert.Equal(TimeSpan.FromMinutes(10), Scheduler.NextInterval);
+ Assert.True(Scheduler.ShouldDropBatch);
+ Assert.False(Scheduler.ShouldDropQueue);
+ }
+
+ [Fact]
+ public void WhenTenFailuresHaveOccurredTheQueueIsDropped()
+ {
+ for (var i = 0; i < 10; ++i)
+ {
+ Assert.False(Scheduler.ShouldDropQueue);
+ Scheduler.MarkFailure();
+ }
+ Assert.True(Scheduler.ShouldDropQueue);
+ }
+
+ [Fact]
+ public void AtTheDefaultIntervalRetriesForTenMinutesBeforeDroppingBatch()
+ {
+ var cumulative = TimeSpan.Zero;
+ do
+ {
+ Scheduler.MarkFailure();
+
+ if (!Scheduler.ShouldDropBatch)
+ cumulative += Scheduler.NextInterval;
+ } while (!Scheduler.ShouldDropBatch);
+
+ Assert.False(Scheduler.ShouldDropQueue);
+ Assert.Equal(TimeSpan.Parse("00:10:32", CultureInfo.InvariantCulture), cumulative);
+ }
+
+ [Fact]
+ public void AtTheDefaultIntervalRetriesForThirtyMinutesBeforeDroppingQueue()
+ {
+ var cumulative = TimeSpan.Zero;
+ do
+ {
+ Scheduler.MarkFailure();
+
+ if (!Scheduler.ShouldDropQueue)
+ cumulative += Scheduler.NextInterval;
+ } while (!Scheduler.ShouldDropQueue);
+
+ Assert.Equal(TimeSpan.Parse("00:30:32", CultureInfo.InvariantCulture), cumulative);
+ }
+}
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/PeriodicBatchingSinkTests.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/PeriodicBatchingSinkTests.cs
index 33de006..22a7fac 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/PeriodicBatchingSinkTests.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/PeriodicBatchingSinkTests.cs
@@ -1,80 +1,112 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
+using Serilog.Debugging;
using Serilog.Sinks.PeriodicBatching.Tests.Support;
using Xunit;
using Serilog.Tests.Support;
+using Xunit.Abstractions;
-namespace Serilog.Sinks.PeriodicBatching.Tests
+namespace Serilog.Sinks.PeriodicBatching.Tests;
+
+public class PeriodicBatchingSinkTests
{
- public class PeriodicBatchingSinkTests
+ static readonly TimeSpan TinyWait = TimeSpan.FromMilliseconds(200);
+ static readonly TimeSpan MicroWait = TimeSpan.FromMilliseconds(1);
+
+ public PeriodicBatchingSinkTests(ITestOutputHelper testOutputHelper)
{
- static readonly TimeSpan TinyWait = TimeSpan.FromMilliseconds(200);
- static readonly TimeSpan MicroWait = TimeSpan.FromMilliseconds(1);
+ SelfLog.Enable(testOutputHelper.WriteLine);
+ }
+
+ [Fact]
+ public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnDispose()
+ {
+ var bs = new InMemoryBatchedSink(TimeSpan.Zero);
+ var pbs = new PeriodicBatchingSink(bs, new() { BatchSizeLimit = 2, Period = TinyWait, EagerlyEmitFirstEvent = true });
+ var evt = Some.InformationEvent();
+ pbs.Emit(evt);
+ pbs.Dispose();
+ Assert.Single(bs.Batches);
+ Assert.Single(bs.Batches[0]);
+ Assert.Same(evt, bs.Batches[0][0]);
+ Assert.True(bs.IsDisposed);
+ Assert.False(bs.WasCalledAfterDisposal);
+ }
- [Fact]
- public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnDispose()
- {
- var bs = new InMemoryBatchedSink(TimeSpan.Zero);
- var pbs = new PeriodicBatchingSink(bs, new PeriodicBatchingSinkOptions
- { BatchSizeLimit = 2, Period = TinyWait, EagerlyEmitFirstEvent = true });
- var evt = Some.InformationEvent();
- pbs.Emit(evt);
- pbs.Dispose();
- Assert.Equal(1, bs.Batches.Count);
- Assert.Equal(1, bs.Batches[0].Count);
- Assert.Same(evt, bs.Batches[0][0]);
- Assert.True(bs.IsDisposed);
- Assert.False(bs.WasCalledAfterDisposal);
- }
-
#if FEATURE_ASYNCDISPOSABLE
[Fact]
- public async ValueTask WhenAnEventIsEnqueuedItIsWrittenToABatchOnDisposeAsync()
+ public async Task WhenAnEventIsEnqueuedItIsWrittenToABatchOnDisposeAsync()
{
var bs = new InMemoryBatchedSink(TimeSpan.Zero);
- var pbs = new PeriodicBatchingSink(bs, new PeriodicBatchingSinkOptions
- { BatchSizeLimit = 2, Period = TinyWait, EagerlyEmitFirstEvent = true });
+ var pbs = new PeriodicBatchingSink(
+ bs, new()
+ {
+ BatchSizeLimit = 2, Period = TinyWait, EagerlyEmitFirstEvent = true
+ });
var evt = Some.InformationEvent();
pbs.Emit(evt);
await pbs.DisposeAsync();
- Assert.Equal(1, bs.Batches.Count);
- Assert.Equal(1, bs.Batches[0].Count);
+ Assert.Single(bs.Batches);
+ Assert.Single(bs.Batches[0]);
Assert.Same(evt, bs.Batches[0][0]);
Assert.True(bs.IsDisposed);
Assert.True(bs.IsDisposedAsync);
Assert.False(bs.WasCalledAfterDisposal);
}
#endif
-
- [Fact]
- public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnTimer()
+
+ [Fact]
+ public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnTimer()
+ {
+ var bs = new InMemoryBatchedSink(TimeSpan.Zero);
+ var pbs = new PeriodicBatchingSink(
+ bs,
+ new()
+ {
+ BatchSizeLimit = 2, Period = TinyWait, EagerlyEmitFirstEvent = true
+ });
+ var evt = Some.InformationEvent();
+ pbs.Emit(evt);
+ Thread.Sleep(TinyWait + TinyWait);
+ bs.Stop();
+ pbs.Dispose();
+ Assert.Single(bs.Batches);
+ Assert.True(bs.IsDisposed);
+ Assert.False(bs.WasCalledAfterDisposal);
+ }
+
+ [Fact]
+ public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnDisposeWhileRunning()
+ {
+ var bs = new InMemoryBatchedSink(TinyWait + TinyWait);
+ var pbs = new PeriodicBatchingSink(bs, new() { BatchSizeLimit = 2, Period = MicroWait, EagerlyEmitFirstEvent = true });
+ var evt = Some.InformationEvent();
+ pbs.Emit(evt);
+ Thread.Sleep(TinyWait);
+ pbs.Dispose();
+ Assert.Single(bs.Batches);
+ Assert.True(bs.IsDisposed);
+ Assert.False(bs.WasCalledAfterDisposal);
+ }
+
+ [Fact]
+ public void ExecutionContextDoesNotFlowToBatchedSink()
+ {
+ var local = new AsyncLocal
{
- var bs = new InMemoryBatchedSink(TimeSpan.Zero);
- var pbs = new PeriodicBatchingSink(bs, new PeriodicBatchingSinkOptions
- { BatchSizeLimit = 2, Period = TinyWait, EagerlyEmitFirstEvent = true });
- var evt = Some.InformationEvent();
- pbs.Emit(evt);
- Thread.Sleep(TinyWait + TinyWait);
- bs.Stop();
- pbs.Dispose();
- Assert.Equal(1, bs.Batches.Count);
- Assert.True(bs.IsDisposed);
- Assert.False(bs.WasCalledAfterDisposal);
- }
+ Value = 5
+ };
- [Fact]
- public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnDisposeWhileRunning()
+ var observed = 17;
+ var bs = new CallbackBatchedSink(_ =>
{
- var bs = new InMemoryBatchedSink(TinyWait + TinyWait);
- var pbs = new PeriodicBatchingSink(bs, new PeriodicBatchingSinkOptions { BatchSizeLimit = 2, Period = MicroWait, EagerlyEmitFirstEvent = true });
- var evt = Some.InformationEvent();
- pbs.Emit(evt);
- Thread.Sleep(TinyWait);
- pbs.Dispose();
- Assert.Equal(1, bs.Batches.Count);
- Assert.True(bs.IsDisposed);
- Assert.False(bs.WasCalledAfterDisposal);
- }
+ observed = local.Value;
+ return Task.CompletedTask;
+ });
+
+ var pbs = new PeriodicBatchingSink(bs, new());
+ var evt = Some.InformationEvent();
+ pbs.Emit(evt);
+ pbs.Dispose();
+
+ Assert.Equal(default(int), observed);
}
-}
+}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/PortableTimerTests.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/PortableTimerTests.cs
deleted file mode 100644
index 05ee73e..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/PortableTimerTests.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Serilog.Debugging;
-using Xunit;
-
-#pragma warning disable 1998
-
-// ReSharper disable AccessToModifiedClosure
-
-namespace Serilog.Sinks.PeriodicBatching.Tests
-{
- public class PortableTimerTests
- {
- [Fact]
- public void WhenItStartsItWaitsUntilHandled_OnDispose()
- {
- var wasCalled = false;
-
- var barrier = new Barrier(participantCount: 2);
-
- using (var timer = new PortableTimer(
- async delegate
- {
- barrier.SignalAndWait();
- await Task.Delay(100);
- wasCalled = true;
- }))
- {
- timer.Start(TimeSpan.Zero);
- barrier.SignalAndWait();
- }
-
- Assert.True(wasCalled);
- }
-
- [Fact]
- public void WhenWaitingShouldCancel_OnDispose()
- {
- var wasCalled = false;
- var writtenToSelfLog = false;
-
- SelfLog.Enable(_ => writtenToSelfLog = true);
-
- using (var timer = new PortableTimer(async delegate { await Task.Delay(50); wasCalled = true; }))
- {
- timer.Start(TimeSpan.FromMilliseconds(20));
- }
-
- Thread.Sleep(100);
-
- Assert.False(wasCalled, "tick handler was called");
- Assert.False(writtenToSelfLog, "message was written to SelfLog");
- }
-
- [Fact]
- public void WhenActiveShouldCancel_OnDispose()
- {
- var wasCalled = false;
- var writtenToSelfLog = false;
-
- SelfLog.Enable(_ => writtenToSelfLog = true);
-
- var barrier = new Barrier(participantCount: 2);
-
- using (var timer = new PortableTimer(
- async token =>
- {
- // ReSharper disable once MethodSupportsCancellation
- barrier.SignalAndWait();
- // ReSharper disable once MethodSupportsCancellation
- await Task.Delay(20);
-
- wasCalled = true;
- Interlocked.MemoryBarrier();
- await Task.Delay(100, token);
- }))
- {
- timer.Start(TimeSpan.FromMilliseconds(20));
- barrier.SignalAndWait();
- }
-
- Thread.Sleep(100);
- Interlocked.MemoryBarrier();
-
- Assert.True(wasCalled, "tick handler wasn't called");
- Assert.True(writtenToSelfLog, "message wasn't written to SelfLog");
- }
-
- [Fact]
- public void WhenDisposedWillThrow_OnStart()
- {
- var wasCalled = false;
- var timer = new PortableTimer(async delegate { wasCalled = true; });
- timer.Start(TimeSpan.FromMilliseconds(100));
- timer.Dispose();
-
- Assert.False(wasCalled);
- Assert.Throws(() => timer.Start(TimeSpan.Zero));
- }
-
- [Fact]
- public void WhenOverlapsShouldProcessOneAtTime_OnTick()
- {
- var userHandlerOverlapped = false;
-
- PortableTimer timer = null;
- timer = new PortableTimer(
- async _ =>
- {
- if (Monitor.TryEnter(timer))
- {
- try
- {
- // ReSharper disable once PossibleNullReferenceException
- timer.Start(TimeSpan.Zero);
- Thread.Sleep(20);
- }
- finally
- {
- Monitor.Exit(timer);
- }
- }
- else
- {
- userHandlerOverlapped = true;
- }
- });
-
- timer.Start(TimeSpan.FromMilliseconds(1));
- Thread.Sleep(50);
- timer.Dispose();
-
- Assert.False(userHandlerOverlapped);
- }
-
- [Fact]
- public void CanBeDisposedFromMultipleThreads()
- {
- PortableTimer timer = null;
- // ReSharper disable once PossibleNullReferenceException
- timer = new PortableTimer(async _ => timer.Start(TimeSpan.FromMilliseconds(1)));
-
- timer.Start(TimeSpan.Zero);
- Thread.Sleep(50);
-
- Parallel.For(0, Environment.ProcessorCount * 2, _ => timer.Dispose());
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Serilog.Sinks.PeriodicBatching.Tests.csproj b/test/Serilog.Sinks.PeriodicBatching.Tests/Serilog.Sinks.PeriodicBatching.Tests.csproj
index e3ff328..1383a8e 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Serilog.Sinks.PeriodicBatching.Tests.csproj
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Serilog.Sinks.PeriodicBatching.Tests.csproj
@@ -1,14 +1,16 @@
- net452;netcoreapp1.1;net6.0
+ net472
+ $(TargetFrameworks);net8.0
../../assets/Serilog.snk
true
true
- false
+ latest
+ enable
-
+
$(DefineConstants);FEATURE_ASYNCDISPOSABLE
@@ -17,15 +19,12 @@
-
-
-
-
-
-
-
-
-
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/CallbackBatchedSink.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/CallbackBatchedSink.cs
new file mode 100644
index 0000000..eaf3459
--- /dev/null
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/CallbackBatchedSink.cs
@@ -0,0 +1,16 @@
+using Serilog.Events;
+
+namespace Serilog.Sinks.PeriodicBatching.Tests.Support;
+
+class CallbackBatchedSink(Func, Task> callback) : IBatchedLogEventSink
+{
+ public Task EmitBatchAsync(IEnumerable batch)
+ {
+ return callback(batch);
+ }
+
+ public Task OnEmptyBatchAsync()
+ {
+ return Task.CompletedTask;
+ }
+}
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/CollectingSink.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/CollectingSink.cs
index 4ff4a7b..ad9f350 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/CollectingSink.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/CollectingSink.cs
@@ -1,21 +1,18 @@
-using System.Collections.Generic;
-using System.Linq;
-using Serilog.Core;
+using Serilog.Core;
using Serilog.Events;
-namespace Serilog.Tests.Support
+namespace Serilog.Tests.Support;
+
+class CollectingSink : ILogEventSink
{
- class CollectingSink : ILogEventSink
- {
- readonly List _events = new List();
+ readonly List _events = new();
- public List Events { get { return _events; } }
+ public List Events => _events;
- public LogEvent SingleEvent { get { return _events.Single(); } }
-
- public void Emit(LogEvent logEvent)
- {
- _events.Add(logEvent);
- }
+ public LogEvent SingleEvent => _events.Single();
+
+ public void Emit(LogEvent logEvent)
+ {
+ _events.Add(logEvent);
}
-}
+}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegateDisposable.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegateDisposable.cs
index fbafd38..993a62a 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegateDisposable.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegateDisposable.cs
@@ -1,24 +1,21 @@
-using System;
+namespace Serilog.Tests.Support;
-namespace Serilog.Tests.Support
+public class DelegateDisposable : IDisposable
{
- public class DelegateDisposable : IDisposable
- {
- private readonly Action _disposeAction;
- private bool _disposed;
+ private readonly Action _disposeAction;
+ private bool _disposed;
- public DelegateDisposable(Action disposeAction)
- {
- _disposeAction = disposeAction;
- }
+ public DelegateDisposable(Action disposeAction)
+ {
+ _disposeAction = disposeAction;
+ }
- public void Dispose()
- {
- if (_disposed)
- return;
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
- _disposeAction();
- _disposed = true;
- }
+ _disposeAction();
+ _disposed = true;
}
}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingEnricher.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingEnricher.cs
index 1d49f31..d8b4fa3 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingEnricher.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingEnricher.cs
@@ -1,22 +1,20 @@
-using System;
-using Serilog.Core;
+using Serilog.Core;
using Serilog.Events;
-namespace Serilog.Tests.Support
+namespace Serilog.Tests.Support;
+
+class DelegatingEnricher : ILogEventEnricher
{
- class DelegatingEnricher : ILogEventEnricher
- {
- readonly Action _enrich;
+ readonly Action _enrich;
- public DelegatingEnricher(Action enrich)
- {
- if (enrich == null) throw new ArgumentNullException(nameof(enrich));
- _enrich = enrich;
- }
+ public DelegatingEnricher(Action enrich)
+ {
+ if (enrich == null) throw new ArgumentNullException(nameof(enrich));
+ _enrich = enrich;
+ }
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
- {
- _enrich(logEvent, propertyFactory);
- }
+ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ {
+ _enrich(logEvent, propertyFactory);
}
-}
+}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingSink.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingSink.cs
index e19ee78..109b067 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingSink.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DelegatingSink.cs
@@ -1,33 +1,30 @@
-using System;
-using Serilog.Core;
+using Serilog.Core;
using Serilog.Events;
-namespace Serilog.Tests.Support
+namespace Serilog.Tests.Support;
+
+public class DelegatingSink : ILogEventSink
{
- public class DelegatingSink : ILogEventSink
- {
- readonly Action _write;
+ readonly Action? _write;
- public DelegatingSink(Action write)
- {
- if (write == null) throw new ArgumentNullException(nameof(write));
- _write = write;
- }
+ public DelegatingSink(Action? write)
+ {
+ _write = write ?? throw new ArgumentNullException(nameof(write));
+ }
- public void Emit(LogEvent logEvent)
- {
- _write(logEvent);
- }
+ public void Emit(LogEvent logEvent)
+ {
+ _write?.Invoke(logEvent);
+ }
- public static LogEvent GetLogEvent(Action writeAction)
- {
- LogEvent result = null;
- var l = new LoggerConfiguration()
- .WriteTo.Sink(new DelegatingSink(le => result = le))
- .CreateLogger();
+ public static LogEvent? GetLogEvent(Action writeAction)
+ {
+ LogEvent? result = null;
+ var l = new LoggerConfiguration()
+ .WriteTo.Sink(new DelegatingSink(le => result = le))
+ .CreateLogger();
- writeAction(l);
- return result;
- }
+ writeAction(l);
+ return result;
}
-}
+}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposableLogger.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposableLogger.cs
index ccff568..ca89603 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposableLogger.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposableLogger.cs
@@ -1,422 +1,419 @@
-using System;
-using System.Collections.Generic;
-using Serilog.Core;
+using Serilog.Core;
using Serilog.Events;
-namespace Serilog.Tests.Support
+namespace Serilog.Tests.Support;
+
+public class DisposableLogger : ILogger, IDisposable
{
- public class DisposableLogger : ILogger, IDisposable
- {
- public bool Disposed { get; set; }
-
- public void Dispose()
- {
- Disposed = true;
- }
-
- public ILogger ForContext(ILogEventEnricher enricher)
- {
- throw new NotImplementedException();
- }
-
- public ILogger ForContext(IEnumerable enrichers)
- {
- throw new NotImplementedException();
- }
-
- public ILogger ForContext(string propertyName, object value, bool destructureObjects = false)
- {
- throw new NotImplementedException();
- }
-
- public ILogger ForContext()
- {
- throw new NotImplementedException();
- }
-
- public ILogger ForContext(Type source)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEvent logEvent)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
- T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, Exception exception, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, Exception exception, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0,
- T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0,
- T1 propertyValue1, T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Write(LogEventLevel level, Exception exception, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public bool IsEnabled(LogEventLevel level)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(Exception exception, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(Exception exception, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
- T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Verbose(Exception exception, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(Exception exception, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(Exception exception, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
- T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Debug(Exception exception, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Information(string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Information(string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Information(string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Information(Exception exception, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Information(Exception exception, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
- T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Information(Exception exception, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(Exception exception, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(Exception exception, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
- T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Warning(Exception exception, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Error(string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Error(string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Error(string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Error(Exception exception, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Error(Exception exception, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
- T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Error(Exception exception, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(Exception exception, string messageTemplate)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(Exception exception, string messageTemplate, T propertyValue)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
- T2 propertyValue2)
- {
- throw new NotImplementedException();
- }
-
- public void Fatal(Exception exception, string messageTemplate, params object[] propertyValues)
- {
- throw new NotImplementedException();
- }
-
- public bool BindMessageTemplate(string messageTemplate, object[] propertyValues, out MessageTemplate parsedTemplate,
- out IEnumerable boundProperties)
- {
- throw new NotImplementedException();
- }
-
- public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty property)
- {
- throw new NotImplementedException();
- }
+ public bool Disposed { get; set; }
+
+ public void Dispose()
+ {
+ Disposed = true;
+ }
+
+ public ILogger ForContext(ILogEventEnricher enricher)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ILogger ForContext(IEnumerable enrichers)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ILogger ForContext(string propertyName, object? value, bool destructureObjects = false)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ILogger ForContext()
+ {
+ throw new NotImplementedException();
+ }
+
+ public ILogger ForContext(Type source)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEvent logEvent)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, Exception? exception, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, Exception? exception, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, Exception? exception, string messageTemplate, T0 propertyValue0,
+ T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, Exception? exception, string messageTemplate, T0 propertyValue0,
+ T1 propertyValue1, T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(LogEventLevel level, Exception? exception, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsEnabled(LogEventLevel level)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(Exception? exception, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(Exception? exception, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Verbose(Exception? exception, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(Exception? exception, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(Exception? exception, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Debug(Exception? exception, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(Exception? exception, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(Exception? exception, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Information(Exception? exception, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(Exception? exception, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(Exception? exception, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Warning(Exception? exception, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(Exception? exception, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(Exception? exception, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Error(Exception? exception, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(Exception? exception, string messageTemplate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(Exception? exception, string messageTemplate, T propertyValue)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Fatal(Exception? exception, string messageTemplate, params object?[]? propertyValues)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool BindMessageTemplate(string messageTemplate, object?[]? propertyValues, out MessageTemplate parsedTemplate,
+ out IEnumerable boundProperties)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool BindProperty(string? propertyName, object? value, bool destructureObjects, out LogEventProperty property)
+ {
+ throw new NotImplementedException();
}
}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposeTrackingSink.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposeTrackingSink.cs
index 8092418..cbabc56 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposeTrackingSink.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/DisposeTrackingSink.cs
@@ -1,20 +1,18 @@
-using System;
-using Serilog.Core;
+using Serilog.Core;
using Serilog.Events;
-namespace Serilog.Tests.Support
+namespace Serilog.Tests.Support;
+
+class DisposeTrackingSink : ILogEventSink, IDisposable
{
- class DisposeTrackingSink : ILogEventSink, IDisposable
- {
- public bool IsDisposed { get; set; }
+ public bool IsDisposed { get; set; }
- public void Emit(LogEvent logEvent)
- {
- }
+ public void Emit(LogEvent logEvent)
+ {
+ }
- public void Dispose()
- {
- IsDisposed = true;
- }
+ public void Dispose()
+ {
+ IsDisposed = true;
}
}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Extensions.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Extensions.cs
index b4fa640..3298d90 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Extensions.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Extensions.cs
@@ -1,12 +1,11 @@
using Serilog.Events;
-namespace Serilog.Tests.Support
+namespace Serilog.Tests.Support;
+
+public static class Extensions
{
- public static class Extensions
+ public static object? LiteralValue(this LogEventPropertyValue @this)
{
- public static object LiteralValue(this LogEventPropertyValue @this)
- {
- return ((ScalarValue)@this).Value;
- }
+ return ((ScalarValue)@this).Value;
}
-}
+}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/InMemoryBatchedSink.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/InMemoryBatchedSink.cs
index 636f126..71ce5d3 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/InMemoryBatchedSink.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/InMemoryBatchedSink.cs
@@ -1,77 +1,64 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
using Serilog.Events;
-namespace Serilog.Sinks.PeriodicBatching.Tests.Support
-{
- sealed class InMemoryBatchedSink : IBatchedLogEventSink, IDisposable
+namespace Serilog.Sinks.PeriodicBatching.Tests.Support;
+
+sealed class InMemoryBatchedSink(TimeSpan batchEmitDelay) : IBatchedLogEventSink, IDisposable
#if FEATURE_ASYNCDISPOSABLE
- , IAsyncDisposable
+ , IAsyncDisposable
#endif
- {
- readonly TimeSpan _batchEmitDelay;
- readonly object _stateLock = new object();
- bool _stopped;
-
- // Postmortem only
- public bool WasCalledAfterDisposal { get; private set; }
- public IList> Batches { get; }
- public bool IsDisposed { get; private set; }
+{
+ readonly object _stateLock = new();
+ bool _stopped;
- public InMemoryBatchedSink(TimeSpan batchEmitDelay)
- {
- _batchEmitDelay = batchEmitDelay;
- Batches = new List>();
- }
+ // Postmortem only
+ public bool WasCalledAfterDisposal { get; private set; }
+ public IList> Batches { get; } = new List>();
+ public bool IsDisposed { get; private set; }
- public void Stop()
+ public void Stop()
+ {
+ lock (_stateLock)
{
- lock (_stateLock)
- {
- _stopped = true;
- }
+ _stopped = true;
}
+ }
- public Task EmitBatchAsync(IEnumerable events)
+ public Task EmitBatchAsync(IEnumerable events)
+ {
+ lock (_stateLock)
{
- lock (_stateLock)
- {
- if (_stopped)
- return Task.FromResult(0);
+ if (_stopped)
+ return Task.FromResult(0);
- if (IsDisposed)
- WasCalledAfterDisposal = true;
+ if (IsDisposed)
+ WasCalledAfterDisposal = true;
- Thread.Sleep(_batchEmitDelay);
- Batches.Add(events.ToList());
- }
-
- return Task.FromResult(0);
+ Thread.Sleep(batchEmitDelay);
+ Batches.Add(events.ToList());
}
- public Task OnEmptyBatchAsync() => Task.FromResult(0);
+ return Task.FromResult(0);
+ }
- public void Dispose()
- {
- lock (_stateLock)
- IsDisposed = true;
- }
+ public Task OnEmptyBatchAsync() => Task.FromResult(0);
+
+ public void Dispose()
+ {
+ lock (_stateLock)
+ IsDisposed = true;
+ }
#if FEATURE_ASYNCDISPOSABLE
- public bool IsDisposedAsync { get; private set; }
+ public bool IsDisposedAsync { get; private set; }
- public ValueTask DisposeAsync()
+ public ValueTask DisposeAsync()
+ {
+ lock (_stateLock)
{
- lock (_stateLock)
- {
- IsDisposedAsync = true;
- Dispose();
- return default;
- }
+ IsDisposedAsync = true;
+ Dispose();
+ return default;
}
-#endif
}
+#endif
}
\ No newline at end of file
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/LegacyDisposeTrackingSink.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/LegacyDisposeTrackingSink.cs
deleted file mode 100644
index f97b130..0000000
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/LegacyDisposeTrackingSink.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-#pragma warning disable CS0618
-
-namespace Serilog.Sinks.PeriodicBatching.Tests.Support
-{
- public class LegacyDisposeTrackingSink : PeriodicBatchingSink
- {
- public bool IsDisposed { get; private set; }
-
- public LegacyDisposeTrackingSink()
- : base(10, TimeSpan.FromMinutes(1))
- {
- }
-
- protected override void Dispose(bool disposing)
- {
- IsDisposed = true;
- }
- }
-}
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Some.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Some.cs
index 0df2968..7586453 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Some.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/Some.cs
@@ -1,87 +1,82 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using Serilog.Events;
+using Serilog.Events;
using Serilog.Parsing;
-namespace Serilog.Tests.Support
+namespace Serilog.Tests.Support;
+
+static class Some
{
- static class Some
+ static int Counter;
+
+ public static int Int()
+ {
+ return Interlocked.Increment(ref Counter);
+ }
+
+ public static decimal Decimal()
+ {
+ return Int() + 0.123m;
+ }
+
+ public static string String(string? tag = null)
+ {
+ return (tag ?? "") + "__" + Int();
+ }
+
+ public static TimeSpan TimeSpan()
+ {
+ return System.TimeSpan.FromMinutes(Int());
+ }
+
+ public static DateTime Instant()
+ {
+ return new DateTime(2012, 10, 28) + TimeSpan();
+ }
+
+ public static DateTimeOffset OffsetInstant()
+ {
+ return new(Instant());
+ }
+
+ public static LogEvent LogEvent(DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information)
+ {
+ return new(timestamp ?? OffsetInstant(), level,
+ null, MessageTemplate(), Enumerable.Empty());
+ }
+
+ public static LogEvent InformationEvent(DateTimeOffset? timestamp = null)
+ {
+ return LogEvent(timestamp);
+ }
+
+ public static LogEvent DebugEvent(DateTimeOffset? timestamp = null)
+ {
+ return LogEvent(timestamp, LogEventLevel.Debug);
+ }
+
+ public static LogEventProperty LogEventProperty()
+ {
+ return new(String(), new ScalarValue(Int()));
+ }
+
+ public static string NonexistentTempFilePath()
+ {
+ return Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".txt");
+ }
+
+ public static string TempFilePath()
+ {
+ return Path.GetTempFileName();
+ }
+
+ public static string TempFolderPath()
+ {
+ var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ Directory.CreateDirectory(dir);
+ return dir;
+ }
+
+ public static MessageTemplate MessageTemplate()
{
- static int Counter;
-
- public static int Int()
- {
- return Interlocked.Increment(ref Counter);
- }
-
- public static decimal Decimal()
- {
- return Int() + 0.123m;
- }
-
- public static string String(string tag = null)
- {
- return (tag ?? "") + "__" + Int();
- }
-
- public static TimeSpan TimeSpan()
- {
- return System.TimeSpan.FromMinutes(Int());
- }
-
- public static DateTime Instant()
- {
- return new DateTime(2012, 10, 28) + TimeSpan();
- }
-
- public static DateTimeOffset OffsetInstant()
- {
- return new DateTimeOffset(Instant());
- }
-
- public static LogEvent LogEvent(DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information)
- {
- return new LogEvent(timestamp ?? OffsetInstant(), level,
- null, MessageTemplate(), Enumerable.Empty());
- }
-
- public static LogEvent InformationEvent(DateTimeOffset? timestamp = null)
- {
- return LogEvent(timestamp, LogEventLevel.Information);
- }
-
- public static LogEvent DebugEvent(DateTimeOffset? timestamp = null)
- {
- return LogEvent(timestamp, LogEventLevel.Debug);
- }
-
- public static LogEventProperty LogEventProperty()
- {
- return new LogEventProperty(String(), new ScalarValue(Int()));
- }
-
- public static string NonexistentTempFilePath()
- {
- return Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".txt");
- }
-
- public static string TempFilePath()
- {
- return Path.GetTempFileName();
- }
-
- public static string TempFolderPath()
- {
- var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- Directory.CreateDirectory(dir);
- return dir;
- }
-
- public static MessageTemplate MessageTemplate()
- {
- return new MessageTemplateParser().Parse(String());
- }
+ return new MessageTemplateParser().Parse(String());
}
-}
+}
\ No newline at end of file
diff --git a/test/TestHarness/Program.cs b/test/TestHarness/Program.cs
new file mode 100644
index 0000000..387245f
--- /dev/null
+++ b/test/TestHarness/Program.cs
@@ -0,0 +1,102 @@
+using System.Diagnostics;
+using Serilog;
+using Serilog.Debugging;
+using Serilog.Events;
+using Serilog.Sinks.PeriodicBatching;
+// ReSharper disable PossibleMultipleEnumeration
+
+// ReSharper disable PossibleLossOfFraction
+
+SelfLog.Enable(Console.Error);
+
+const int producers = 3;
+const int countPerProducer = 1000;
+var producerInterval = TimeSpan.FromMilliseconds(90);
+
+const int batchSize = 50;
+var batchInterval = TimeSpan.FromMilliseconds(100);
+var maximumBufferDelay = TimeSpan.FromMilliseconds(80);
+
+var sinkFailAfterEvents = (int?)50;
+
+Console.WriteLine($"Producers will take {countPerProducer * producerInterval} to complete");
+Console.WriteLine($"Producers will generate a total of {countPerProducer * producers} events");
+Console.WriteLine($"Consumer will take {countPerProducer * producers / batchSize * batchInterval} to write batches");
+
+var options = new PeriodicBatchingSinkOptions
+{
+ EagerlyEmitFirstEvent = false,
+ QueueLimit = null,
+ BatchSizeLimit = batchSize,
+ Period = maximumBufferDelay
+};
+
+var batchedSink = new EmptyBatchedSink(batchInterval, sinkFailAfterEvents);
+
+var logger = new LoggerConfiguration()
+ .WriteTo.Sink(new PeriodicBatchingSink(batchedSink, options))
+ .CreateLogger();
+
+var threads = Enumerable.Range(0, producers)
+ .Select(id => new Thread(() => RunProducer(id, logger, producerInterval, countPerProducer)))
+ .ToArray();
+
+var sw = Stopwatch.StartNew();
+
+foreach (var thread in threads)
+{
+ thread.Start();
+}
+
+foreach (var thread in threads)
+{
+ thread.Join();
+}
+
+Console.WriteLine($"All producers done in {sw.Elapsed}");
+
+await logger.DisposeAsync();
+
+Console.WriteLine($"All batches processed in {sw.Elapsed}");
+Console.WriteLine($"Sink saw {batchedSink.EventCount} events in {batchedSink.BatchCount} batches");
+Console.WriteLine($"Largest batch was {batchedSink.MaxBatchSize}; smallest was {batchedSink.MinBatchSize}");
+
+static void RunProducer(int id, ILogger logger, TimeSpan interval, int count)
+{
+ for (var i = 0; i < count; ++i)
+ {
+ logger.Information("Hello number {N} from producer {Id}!", i, id);
+ Thread.Sleep(interval);
+ }
+}
+
+class EmptyBatchedSink(TimeSpan flushDelay, int? failAfterEvents): IBatchedLogEventSink
+{
+ bool _failureDone;
+
+ public int BatchCount { get; private set; }
+ public int EventCount { get; private set; }
+ public int MaxBatchSize { get; private set; }
+ public int MinBatchSize { get; private set; } = int.MaxValue;
+
+ public async Task EmitBatchAsync(IEnumerable batch)
+ {
+ if (failAfterEvents is { } f && EventCount >= f && !_failureDone)
+ {
+ _failureDone = true;
+ throw new Exception("This batch failed.");
+ }
+
+ BatchCount++;
+ EventCount += batch.Count();
+ MinBatchSize = Math.Min(MinBatchSize, batch.Count());
+ MaxBatchSize = Math.Max(MaxBatchSize, batch.Count());
+
+ await Task.Delay(flushDelay);
+ }
+
+ public Task OnEmptyBatchAsync()
+ {
+ return Task.CompletedTask;
+ }
+}
diff --git a/test/TestHarness/TestHarness.csproj b/test/TestHarness/TestHarness.csproj
new file mode 100644
index 0000000..6f889fe
--- /dev/null
+++ b/test/TestHarness/TestHarness.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+