diff --git a/Build.ps1 b/Build.ps1
index 07fcb2c..b9ed918 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -1,26 +1,60 @@
+Write-Output "build: Build started"
+
+& dotnet --info
+& dotnet --list-sdks
+
Push-Location $PSScriptRoot
-if(Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse }
+if(Test-Path .\artifacts) {
+ Write-Output "build: Cleaning .\artifacts"
+ Remove-Item .\artifacts -Force -Recurse
+}
& dotnet restore --no-cache
-$branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL];
-$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL];
-$suffix = @{ $true = ""; $false = "$branch-$revision"}[$branch -eq "main" -and $revision -ne "local"]
+$branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$NULL -ne $env:APPVEYOR_REPO_BRANCH];
+$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$NULL -ne $env:APPVEYOR_BUILD_NUMBER];
+$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"]
+$commitHash = $(git rev-parse --short HEAD)
+$buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""]
+
+Write-Output "build: Package version suffix is $suffix"
+Write-Output "build: Build version suffix is $buildSuffix"
-foreach ($src in ls src/Serilog.*) {
+foreach ($src in Get-ChildItem src/*) {
Push-Location $src
- & dotnet pack -c Release -o ..\..\.\artifacts --version-suffix=$suffix --include-source
- if($LASTEXITCODE -ne 0) { exit 1 }
+ Write-Output "build: Packaging project in $src"
+
+ & dotnet build -c Release --version-suffix=$buildSuffix /p:ContinuousIntegrationBuild=true
+
+ if($suffix) {
+ & dotnet pack -c Release --no-build -o ..\..\artifacts --version-suffix=$suffix
+ } else {
+ & dotnet pack -c Release --no-build -o ..\..\artifacts
+ }
+ if($LASTEXITCODE -ne 0) { exit 1 }
Pop-Location
}
-foreach ($test in ls test/Serilog.*.Tests) {
+foreach ($test in Get-ChildItem test/*.Tests) {
Push-Location $test
+ 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 }
Pop-Location
diff --git a/appveyor.yml b/appveyor.yml
index 37b11c9..c04c69b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,15 +1,15 @@
version: '{build}'
skip_tags: true
-image: Visual Studio 2019
+image: Visual Studio 2022
build_script:
-- ps: ./Build.ps1
+- pwsh: ./Build.ps1
test: off
artifacts:
- path: artifacts/Serilog.*.nupkg
deploy:
- provider: NuGet
api_key:
- secure: rbdBqxBpLt4MkB+mrDOYNDOd8aVZ1zMkysaVNAXNKnC41FYifzX3l9LM8DCrUWU5
+ secure: oemq1E4zMR+LKQyrR83ZLcugPpZtl5OMKjtpMy/mbPEwuFGS+Oe46427D9KoHYD8
skip_symbols: true
on:
branch: /^(main|dev)$/
diff --git a/assets/Serilog.svg b/assets/Serilog.svg
deleted file mode 100644
index 963d20a..0000000
--- a/assets/Serilog.svg
+++ /dev/null
@@ -1,189 +0,0 @@
-
-
\ No newline at end of file
diff --git a/assets/icon.png b/assets/icon.png
new file mode 100644
index 0000000..9bf45a3
Binary files /dev/null and b/assets/icon.png differ
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..ba75026
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "6.0.300",
+ "allowPrerelease": false,
+ "rollForward": "latestFeature"
+ }
+}
diff --git a/serilog-sinks-periodicbatching.sln b/serilog-sinks-periodicbatching.sln
index 97f5600..f92a8b8 100644
--- a/serilog-sinks-periodicbatching.sln
+++ b/serilog-sinks-periodicbatching.sln
@@ -7,14 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5E1-DEB9-4A04-8BAB-24EC7240ADAF}"
ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- .gitignore = .gitignore
- appveyor.yml = appveyor.yml
- Build.ps1 = Build.ps1
- CHANGES.md = CHANGES.md
- README.md = README.md
- RunPerfTests.ps1 = RunPerfTests.ps1
assets\Serilog.snk = assets\Serilog.snk
+ assets\icon.png = assets\icon.png
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F6E07A13-B9D3-4019-B25A-DE1F6C17E108}"
@@ -25,6 +19,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.PeriodicBatch
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
+ .gitignore = .gitignore
+ appveyor.yml = appveyor.yml
+ Build.ps1 = Build.ps1
+ CHANGES.md = CHANGES.md
+ README.md = README.md
+ RunPerfTests.ps1 = RunPerfTests.ps1
+ global.json = global.json
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs b/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs
index e03a26b..ff8192e 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Properties/AssemblyInfo.cs
@@ -2,7 +2,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
-[assembly: AssemblyVersion("2.0.0.0")]
+[assembly: AssemblyVersion("3.0.0.0")]
[assembly: CLSCompliant(true)]
diff --git a/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj b/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj
index 601b6c0..8cbb235 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj
+++ b/src/Serilog.Sinks.PeriodicBatching/Serilog.Sinks.PeriodicBatching.csproj
@@ -1,41 +1,53 @@
-
+
- The periodic batching sink for Serilog
- 2.3.1
+ Buffer batches of log events to be flushed asynchronously.
+ 3.0.0
Serilog Contributors
net45;netstandard1.1;netstandard1.2;netstandard2.0;netstandard2.1
true
- Serilog.Sinks.PeriodicBatching
Serilog
../../assets/Serilog.snk
true
true
- Serilog.Sinks.PeriodicBatching
serilog;batching;timer
- http://serilog.net/images/serilog-sink-nuget.png
- http://serilog.net
- http://www.apache.org/licenses/LICENSE-2.0
+ 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
+
+
+
+ $(DefineConstants);FEATURE_THREADING_TIMER
+
+
+
+ $(DefineConstants);FEATURE_THREADING_TIMER;FEATURE_EXECUTION_CONTEXT
+
+
+
+ $(DefineConstants);FEATURE_THREADING_TIMER;FEATURE_EXECUTION_CONTEXT;FEATURE_ASYNCDISPOSABLE
-
+
+
-
-
- $(DefineConstants);THREADING_TIMER
-
-
-
- $(DefineConstants);THREADING_TIMER;EXECUTION_CONTEXT
-
+
+
+
+
+
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BatchedConnectionStatus.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BatchedConnectionStatus.cs
index 39d3769..5a3eda4 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BatchedConnectionStatus.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BatchedConnectionStatus.cs
@@ -1,4 +1,4 @@
-// Copyright 2013-2020 Serilog Contributors
+// 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.
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
index 2cb02fc..23ca933 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
@@ -1,4 +1,4 @@
-// Copyright 2013-2020 Serilog Contributors
+// 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.
@@ -22,7 +22,7 @@ class BoundedConcurrentQueue
{
const int Unbounded = -1;
- readonly ConcurrentQueue _queue = new ConcurrentQueue();
+ readonly ConcurrentQueue _queue = new();
readonly int _queueLimit;
int _counter;
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs
index 27357ea..e4672c9 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/IBatchedLogEventSink.cs
@@ -1,4 +1,4 @@
-// Copyright 2013-2020 Serilog Contributors
+// 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.
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
index 78fefd7..30a338f 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
@@ -1,4 +1,4 @@
-// Copyright 2013-2020 Serilog Contributors
+// 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.
@@ -18,15 +18,13 @@
using Serilog.Core;
using Serilog.Debugging;
using Serilog.Events;
-using System.Threading;
-// ReSharper disable MemberCanBePrivate.Global, UnusedMember.Global, VirtualMemberNeverOverridden.Global, ClassWithVirtualMembersNeverInherited.Global
+// ReSharper disable UnusedParameter.Global, ConvertIfStatementToConditionalTernaryExpression, MemberCanBePrivate.Global, UnusedMember.Global, VirtualMemberNeverOverridden.Global, ClassWithVirtualMembersNeverInherited.Global, SuspiciousTypeConversion.Global
namespace Serilog.Sinks.PeriodicBatching
{
///
- /// Base class for sinks that log events in batches. Batching is
- /// triggered asynchronously on a timer.
+ /// Buffers log events into batches for background flushing.
///
///
/// To avoid unbounded memory growth, events are discarded after attempting
@@ -34,22 +32,19 @@ namespace Serilog.Sinks.PeriodicBatching
/// 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
+ 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 Queue();
+ readonly Queue _waitingBatch = new();
- readonly object _stateLock = new object();
+ readonly object _stateLock = new();
readonly PortableTimer _timer;
@@ -64,57 +59,9 @@ public class PeriodicBatchingSink : ILogEventSink, IDisposable, IBatchedLogEvent
/// 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;
- }
-
- ///
- /// 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 ? (int?)null : queueLimit
- })
- {
- _batchedLogEventSink = this;
- }
-
- PeriodicBatchingSink(PeriodicBatchingSinkOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
+ _batchedLogEventSink = batchedSink ?? throw new ArgumentNullException(nameof(batchedSink));
if (options.BatchSizeLimit <= 0)
throw new ArgumentOutOfRangeException(nameof(options), "The batch size limit must be greater than zero.");
@@ -125,10 +72,11 @@ protected PeriodicBatchingSink(int batchSizeLimit, TimeSpan period, int queueLim
_queue = new BoundedConcurrentQueue(options.QueueLimit);
_status = new BatchedConnectionStatus(options.Period);
_eagerlyEmitFirstEvent = options.EagerlyEmitFirstEvent;
- _timer = new PortableTimer(cancel => OnTick());
+ _timer = new PortableTimer(_ => OnTick());
}
- void CloseAndFlush()
+ ///
+ public void Dispose()
{
lock (_stateLock)
{
@@ -141,70 +89,34 @@ void CloseAndFlush()
_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
- ResetSyncContextAndWait(OnTick);
+ // so we prevent possible deadlocks here for sync-over-async downstream implementations.
+ TaskUtil.ResetSyncContextAndWait(OnTick);
- if (_batchedLogEventSink != this)
- (_batchedLogEventSink as IDisposable)?.Dispose();
+ (_batchedLogEventSink as IDisposable)?.Dispose();
}
-
- static void ResetSyncContextAndWait(Func taskFactory)
+
+#if FEATURE_ASYNCDISPOSABLE
+ ///
+ public async ValueTask DisposeAsync()
{
- var prevContext = SynchronizationContext.Current;
- SynchronizationContext.SetSynchronizationContext(null);
- try
- {
- taskFactory().Wait();
- }
- finally
+ lock (_stateLock)
{
- SynchronizationContext.SetSynchronizationContext(prevContext);
- }
- }
+ if (!_started || _unloading)
+ return;
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- /// 2
- public void Dispose()
- {
- Dispose(true);
- }
+ _unloading = true;
+ }
- ///
- /// 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)
- {
- if (!disposing) return;
- CloseAndFlush();
- }
+ _timer.Dispose();
- ///
- /// 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)
- {
- }
+ await OnTick().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);
+ if (_batchedLogEventSink is IAsyncDisposable asyncDisposable)
+ await asyncDisposable.DisposeAsync().ConfigureAwait(false);
+ else
+ (_batchedLogEventSink as IDisposable)?.Dispose();
}
+#endif
async Task OnTick()
{
@@ -216,17 +128,16 @@ async Task OnTick()
while (_waitingBatch.Count < _batchSizeLimit &&
_queue.TryDequeue(out var next))
{
- if (CanInclude(next))
- _waitingBatch.Enqueue(next);
+ _waitingBatch.Enqueue(next);
}
if (_waitingBatch.Count == 0)
{
- await _batchedLogEventSink.OnEmptyBatchAsync();
+ await _batchedLogEventSink.OnEmptyBatchAsync().ConfigureAwait(false);
return;
}
- await _batchedLogEventSink.EmitBatchAsync(_waitingBatch);
+ await _batchedLogEventSink.EmitBatchAsync(_waitingBatch).ConfigureAwait(false);
batchWasFull = _waitingBatch.Count >= _batchSizeLimit;
_waitingBatch.Clear();
@@ -308,44 +219,5 @@ public void Emit(LogEvent logEvent)
_queue.TryEnqueue(logEvent);
}
-
- ///
- /// 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)
- {
- return true;
- }
-
- ///
- /// 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()
- {
- }
-
- ///
- /// 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
- {
- // ReSharper disable once MethodHasAsyncOverload
- OnEmptyBatch();
- }
-
- Task IBatchedLogEventSink.EmitBatchAsync(IEnumerable batch) => EmitBatchAsync(batch);
- Task IBatchedLogEventSink.OnEmptyBatchAsync() => OnEmptyBatchAsync();
}
}
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs
index 4bec61d..5acd457 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PeriodicBatchingSinkOptions.cs
@@ -1,4 +1,4 @@
-// Copyright 2013-2020 Serilog Contributors
+// 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.
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PortableTimer.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PortableTimer.cs
index 2752465..ac6e44d 100644
--- a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PortableTimer.cs
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/PortableTimer.cs
@@ -1,4 +1,4 @@
-// Copyright 2013-2020 Serilog Contributors
+// 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.
@@ -21,12 +21,12 @@ namespace Serilog.Sinks.PeriodicBatching
{
class PortableTimer : IDisposable
{
- readonly object _stateLock = new object();
+ readonly object _stateLock = new();
readonly Func _onTick;
- readonly CancellationTokenSource _cancel = new CancellationTokenSource();
+ readonly CancellationTokenSource _cancel = new();
-#if THREADING_TIMER
+#if FEATURE_THREADING_TIMER
readonly Timer _timer;
#endif
@@ -35,12 +35,10 @@ class PortableTimer : IDisposable
public PortableTimer(Func onTick)
{
- if (onTick == null) throw new ArgumentNullException(nameof(onTick));
+ _onTick = onTick ?? throw new ArgumentNullException(nameof(onTick));
- _onTick = onTick;
-
-#if THREADING_TIMER
-#if EXECUTION_CONTEXT
+#if FEATURE_THREADING_TIMER
+#if FEATURE_EXECUTION_CONTEXT
using (ExecutionContext.SuppressFlow())
#endif
_timer = new Timer(_ => OnTick(), null, Timeout.Infinite, Timeout.Infinite);
@@ -56,7 +54,7 @@ public void Start(TimeSpan interval)
if (_disposed)
throw new ObjectDisposedException(nameof(PortableTimer));
-#if THREADING_TIMER
+#if FEATURE_THREADING_TIMER
_timer.Change(interval, Timeout.InfiniteTimeSpan);
#else
Task.Delay(interval, _cancel.Token)
@@ -131,7 +129,7 @@ public void Dispose()
Monitor.Wait(_stateLock);
}
-#if THREADING_TIMER
+#if FEATURE_THREADING_TIMER
_timer.Dispose();
#endif
@@ -139,4 +137,4 @@ public void Dispose()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/TaskUtil.cs b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/TaskUtil.cs
new file mode 100644
index 0000000..f9a2aad
--- /dev/null
+++ b/src/Serilog.Sinks.PeriodicBatching/Sinks/PeriodicBatching/TaskUtil.cs
@@ -0,0 +1,36 @@
+// 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.Tests/PeriodicBatchingSinkTests.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/PeriodicBatchingSinkTests.cs
index a514e8f..33de006 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/PeriodicBatchingSinkTests.cs
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/PeriodicBatchingSinkTests.cs
@@ -1,84 +1,19 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Serilog.Sinks.PeriodicBatching.Tests.Support;
using Xunit;
-using Serilog.Events;
using Serilog.Tests.Support;
namespace Serilog.Sinks.PeriodicBatching.Tests
{
- class InMemoryBatchedSink : IBatchedLogEventSink, IDisposable
- {
- 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; }
-
- public InMemoryBatchedSink(TimeSpan batchEmitDelay)
- {
- _batchEmitDelay = batchEmitDelay;
- Batches = new List>();
- }
-
- public void Stop()
- {
- lock (_stateLock)
- {
- _stopped = true;
- }
- }
-
- public Task EmitBatchAsync(IEnumerable events)
- {
- lock (_stateLock)
- {
- if (_stopped)
- return Task.FromResult(0);
-
- if (IsDisposed)
- WasCalledAfterDisposal = true;
-
- Thread.Sleep(_batchEmitDelay);
- Batches.Add(events.ToList());
- }
-
- return Task.FromResult(0);
- }
-
- public Task OnEmptyBatchAsync() => Task.FromResult(0);
-
- public void Dispose()
- {
- lock (_stateLock)
- IsDisposed = true;
- }
- }
-
- class NullBatchedSink : PeriodicBatchingSink
- {
- public NullBatchedSink(int batchSizeLimit, TimeSpan period, int queueLimit)
-#pragma warning disable 618
- : base(batchSizeLimit, period, queueLimit)
-#pragma warning restore 618
- {
- }
- }
-
public class PeriodicBatchingSinkTests
{
static readonly TimeSpan TinyWait = TimeSpan.FromMilliseconds(200);
static readonly TimeSpan MicroWait = TimeSpan.FromMilliseconds(1);
- // Some very, very approximate tests here :)
-
[Fact]
- public void WhenAnEventIsEnqueuedItIsWrittenToABatch_OnFlush()
+ public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnDispose()
{
var bs = new InMemoryBatchedSink(TimeSpan.Zero);
var pbs = new PeriodicBatchingSink(bs, new PeriodicBatchingSinkOptions
@@ -92,9 +27,28 @@ public void WhenAnEventIsEnqueuedItIsWrittenToABatch_OnFlush()
Assert.True(bs.IsDisposed);
Assert.False(bs.WasCalledAfterDisposal);
}
-
+
+#if FEATURE_ASYNCDISPOSABLE
+ [Fact]
+ public async ValueTask WhenAnEventIsEnqueuedItIsWrittenToABatchOnDisposeAsync()
+ {
+ 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);
+ await pbs.DisposeAsync();
+ 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.True(bs.IsDisposedAsync);
+ Assert.False(bs.WasCalledAfterDisposal);
+ }
+#endif
+
[Fact]
- public void WhenAnEventIsEnqueuedItIsWrittenToABatch_OnTimer()
+ public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnTimer()
{
var bs = new InMemoryBatchedSink(TimeSpan.Zero);
var pbs = new PeriodicBatchingSink(bs, new PeriodicBatchingSinkOptions
@@ -110,7 +64,7 @@ public void WhenAnEventIsEnqueuedItIsWrittenToABatch_OnTimer()
}
[Fact]
- public void WhenAnEventIsEnqueuedItIsWrittenToABatch_FlushWhileRunning()
+ public void WhenAnEventIsEnqueuedItIsWrittenToABatchOnDisposeWhileRunning()
{
var bs = new InMemoryBatchedSink(TinyWait + TinyWait);
var pbs = new PeriodicBatchingSink(bs, new PeriodicBatchingSinkOptions { BatchSizeLimit = 2, Period = MicroWait, EagerlyEmitFirstEvent = true });
@@ -122,13 +76,5 @@ public void WhenAnEventIsEnqueuedItIsWrittenToABatch_FlushWhileRunning()
Assert.True(bs.IsDisposed);
Assert.False(bs.WasCalledAfterDisposal);
}
-
- [Fact]
- public void SubclassesCanBeConstructedUsingNoQueueLimitConstant()
- {
-#pragma warning disable 618
- var _ = new NullBatchedSink(batchSizeLimit: 100, TimeSpan.FromSeconds(2), queueLimit: PeriodicBatchingSink.NoQueueLimit);
-#pragma warning restore 618
- }
}
}
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 fa4a470..a258351 100644
--- a/test/Serilog.Sinks.PeriodicBatching.Tests/Serilog.Sinks.PeriodicBatching.Tests.csproj
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Serilog.Sinks.PeriodicBatching.Tests.csproj
@@ -1,12 +1,16 @@
- net452;netcoreapp1.1
+ net452;netcoreapp1.1;net6.0
../../assets/Serilog.snk
true
true
+
+ $(DefineConstants);FEATURE_ASYNCDISPOSABLE
+
+
@@ -22,8 +26,4 @@
-
-
-
-
diff --git a/test/Serilog.Sinks.PeriodicBatching.Tests/Support/InMemoryBatchedSink.cs b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/InMemoryBatchedSink.cs
new file mode 100644
index 0000000..636f126
--- /dev/null
+++ b/test/Serilog.Sinks.PeriodicBatching.Tests/Support/InMemoryBatchedSink.cs
@@ -0,0 +1,77 @@
+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
+#if FEATURE_ASYNCDISPOSABLE
+ , 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; }
+
+ public InMemoryBatchedSink(TimeSpan batchEmitDelay)
+ {
+ _batchEmitDelay = batchEmitDelay;
+ Batches = new List>();
+ }
+
+ public void Stop()
+ {
+ lock (_stateLock)
+ {
+ _stopped = true;
+ }
+ }
+
+ public Task EmitBatchAsync(IEnumerable events)
+ {
+ lock (_stateLock)
+ {
+ if (_stopped)
+ return Task.FromResult(0);
+
+ if (IsDisposed)
+ WasCalledAfterDisposal = true;
+
+ Thread.Sleep(_batchEmitDelay);
+ Batches.Add(events.ToList());
+ }
+
+ return Task.FromResult(0);
+ }
+
+ public Task OnEmptyBatchAsync() => Task.FromResult(0);
+
+ public void Dispose()
+ {
+ lock (_stateLock)
+ IsDisposed = true;
+ }
+
+#if FEATURE_ASYNCDISPOSABLE
+ public bool IsDisposedAsync { get; private set; }
+
+ public ValueTask DisposeAsync()
+ {
+ lock (_stateLock)
+ {
+ IsDisposedAsync = true;
+ Dispose();
+ return default;
+ }
+ }
+#endif
+ }
+}
\ No newline at end of file