diff --git a/sources/Capsule.Core/CapsuleHost.cs b/sources/Capsule.Core/CapsuleHost.cs index 5cb08f9..04dd631 100644 --- a/sources/Capsule.Core/CapsuleHost.cs +++ b/sources/Capsule.Core/CapsuleHost.cs @@ -48,6 +48,8 @@ await Task.WhenAny(_taskChannel.Reader.WaitToReadAsync(stoppingToken).AsTask(), // Shutting down, so await all tasks await Task.WhenAll(_invocationLoopTasks).ConfigureAwait(false); + + logger.LogDebug("Capsule host terminated"); } private async Task SeparateCompletedTasksAsync() diff --git a/sources/Capsule.Core/DefaultInvocationLoopFactory.cs b/sources/Capsule.Core/DefaultInvocationLoopFactory.cs index c5480c7..755541e 100644 --- a/sources/Capsule.Core/DefaultInvocationLoopFactory.cs +++ b/sources/Capsule.Core/DefaultInvocationLoopFactory.cs @@ -4,8 +4,8 @@ namespace Capsule; public class DefaultInvocationLoopFactory(ICapsuleLogger logger) : ICapsuleInvocationLoopFactory { - public ICapsuleInvocationLoop Create(ChannelReader> reader) + public ICapsuleInvocationLoop Create(ChannelReader> reader, Type capsuleType) { - return new InvocationLoop(reader, logger); + return new InvocationLoop(reader, capsuleType, logger); } } diff --git a/sources/Capsule.Core/DefaultSynchronizerFactory.cs b/sources/Capsule.Core/DefaultSynchronizerFactory.cs index bbdb470..624aff2 100644 --- a/sources/Capsule.Core/DefaultSynchronizerFactory.cs +++ b/sources/Capsule.Core/DefaultSynchronizerFactory.cs @@ -6,12 +6,14 @@ public class DefaultSynchronizerFactory( { public ICapsuleSynchronizer Create(object capsuleImpl, ICapsuleHost host) { + var capsuleType = capsuleImpl.GetType(); + var queue = queueFactory.CreateSynchronizerQueue(); - var synchronizer = new CapsuleSynchronizer(queue.Writer, capsuleImpl.GetType()); + var synchronizer = new CapsuleSynchronizer(queue.Writer, capsuleType); ApplyFeatures(capsuleImpl, synchronizer); - host.Register(invocationLoopFactory.Create(queue.Reader)); + host.Register(invocationLoopFactory.Create(queue.Reader, capsuleType)); return synchronizer; } diff --git a/sources/Capsule.Core/ICapsuleInvocationLoopFactory.cs b/sources/Capsule.Core/ICapsuleInvocationLoopFactory.cs index 801518f..401bb1c 100644 --- a/sources/Capsule.Core/ICapsuleInvocationLoopFactory.cs +++ b/sources/Capsule.Core/ICapsuleInvocationLoopFactory.cs @@ -4,5 +4,5 @@ namespace Capsule; public interface ICapsuleInvocationLoopFactory { - ICapsuleInvocationLoop Create(ChannelReader> reader); + ICapsuleInvocationLoop Create(ChannelReader> reader, Type capsuleType); } diff --git a/sources/Capsule.Core/ICapsuleLogger.cs b/sources/Capsule.Core/ICapsuleLogger.cs index 33111e5..a2dfa26 100644 --- a/sources/Capsule.Core/ICapsuleLogger.cs +++ b/sources/Capsule.Core/ICapsuleLogger.cs @@ -3,7 +3,7 @@ // ReSharper disable once UnusedTypeParameter public interface ICapsuleLogger { - void LogDebug(string message); + void LogDebug(string message, params object?[] args); void LogWarning(Exception exception, string message); diff --git a/sources/Capsule.Core/InvocationLoop.cs b/sources/Capsule.Core/InvocationLoop.cs index a50750e..50d6839 100644 --- a/sources/Capsule.Core/InvocationLoop.cs +++ b/sources/Capsule.Core/InvocationLoop.cs @@ -2,11 +2,16 @@ namespace Capsule; -internal class InvocationLoop(ChannelReader> reader, ICapsuleLogger logger) +internal class InvocationLoop( + ChannelReader> reader, + Type capsuleType, + ICapsuleLogger logger) : ICapsuleInvocationLoop { public async Task RunAsync(CancellationToken cancellationToken) { + logger.LogDebug("Starting invocation loop for capsule {CapsuleType}...", capsuleType.FullName); + while (!cancellationToken.IsCancellationRequested) { try @@ -33,6 +38,8 @@ public async Task RunAsync(CancellationToken cancellationToken) await ExecuteAsync(f).ConfigureAwait(false); } } + + logger.LogDebug("Invocation loop for capsule {CapsuleType} terminated", capsuleType.FullName); } private async Task ExecuteAsync(Func invocation) diff --git a/sources/Capsule.Extensions.DependencyInjection/CapsuleBackgroundService.cs b/sources/Capsule.Extensions.DependencyInjection/CapsuleBackgroundService.cs index b1c0eaa..d1765a6 100644 --- a/sources/Capsule.Extensions.DependencyInjection/CapsuleBackgroundService.cs +++ b/sources/Capsule.Extensions.DependencyInjection/CapsuleBackgroundService.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Capsule.Extensions.DependencyInjection; @@ -6,10 +7,14 @@ namespace Capsule.Extensions.DependencyInjection; /// A hosted service that contains an and shuts it down when application shutdown is /// initiated. /// -public class CapsuleBackgroundService(CapsuleHost host) : BackgroundService +public class CapsuleBackgroundService(CapsuleHost host, ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + logger.LogDebug("Starting Capsule background service..."); + await host.RunAsync(stoppingToken).ConfigureAwait(false); + + logger.LogDebug("Capsule background service terminated"); } } diff --git a/sources/Capsule.Extensions.DependencyInjection/LoggingAdapter.cs b/sources/Capsule.Extensions.DependencyInjection/LoggingAdapter.cs index 8b41b67..a6544a5 100644 --- a/sources/Capsule.Extensions.DependencyInjection/LoggingAdapter.cs +++ b/sources/Capsule.Extensions.DependencyInjection/LoggingAdapter.cs @@ -4,9 +4,9 @@ namespace Capsule.Extensions.DependencyInjection; public class LoggingAdapter(ILogger logger) : ICapsuleLogger { - public void LogDebug(string message) + public void LogDebug(string message, params object?[] args) { - logger.LogDebug(message); + logger.LogDebug(message, args); } public void LogWarning(Exception exception, string message) diff --git a/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitCompletionTest.cs b/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitCompletionTest.cs index 7405571..037e063 100644 --- a/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitCompletionTest.cs +++ b/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitCompletionTest.cs @@ -1,5 +1,9 @@ using Capsule.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using Moq; + using Shouldly; namespace Capsule.Test.AutomatedTests.UnitTests; @@ -21,7 +25,9 @@ public async Task Await_completion_returns_result_when_method_ran_to_completion_ private static async Task TestSuccessfulCompletion(Func> testSubjectCall) { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -65,7 +71,9 @@ public async Task Await_completion_throws_when_method_ran_to_completion_with_exc private static async Task TestException(Func> testSubjectCall) { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -111,7 +119,9 @@ public async Task Await_completion_throws_when_method_is_cancelled_value_task() private static async Task TestCancellation(Func> testSubjectCall) { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitEnqueueingTest.cs b/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitEnqueueingTest.cs index 6b9364a..a721318 100644 --- a/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitEnqueueingTest.cs +++ b/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitEnqueueingTest.cs @@ -1,5 +1,9 @@ using Capsule.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using Moq; + using Shouldly; namespace Capsule.Test.AutomatedTests.UnitTests; @@ -21,7 +25,9 @@ public async Task Await_enqueueing_returns_when_invocation_was_enqueued_value_ta private static async Task TestSuccessful(Func testSubjectCall) { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -76,7 +82,9 @@ public async Task Await_enqueueing_does_not_throw_exception_when_capsule_method_ private static async Task TestException(Func testSubjectCall) { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -116,7 +124,9 @@ public async Task Await_enqueueing_does_not_throw_exception_when_capsule_method_ private static async Task TestCancellation(Func testSubjectCall) { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitReceptionTest.cs b/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitReceptionTest.cs index d964e33..a81d34d 100644 --- a/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitReceptionTest.cs +++ b/sources/Capsule.Test/AutomatedTests/UnitTests/AwaitReceptionTest.cs @@ -1,5 +1,9 @@ using Capsule.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using Moq; + using Shouldly; namespace Capsule.Test.AutomatedTests.UnitTests; @@ -10,7 +14,9 @@ public class AwaitReceptionTest public async Task Await_reception_returns_when_invocation_was_started() { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -53,7 +59,9 @@ public async Task Await_reception_returns_when_invocation_was_started() public async Task Await_reception_does_not_throw_exception_when_capsule_method_throws() { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -82,7 +90,9 @@ public async Task Await_reception_does_not_throw_exception_when_capsule_method_t public async Task Await_reception_does_not_throw_exception_when_capsule_method_is_cancelled() { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/sources/Capsule.Test/AutomatedTests/UnitTests/ConcurrencyTest.cs b/sources/Capsule.Test/AutomatedTests/UnitTests/ConcurrencyTest.cs index 5c345e9..41102bf 100644 --- a/sources/Capsule.Test/AutomatedTests/UnitTests/ConcurrencyTest.cs +++ b/sources/Capsule.Test/AutomatedTests/UnitTests/ConcurrencyTest.cs @@ -1,6 +1,10 @@ using Capsule.Attribution; using Capsule.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using Moq; + using Shouldly; namespace Capsule.Test.AutomatedTests.UnitTests; @@ -11,7 +15,9 @@ public class ConcurrencyTest public async Task Capsule_does_not_produce_inconsistent_state_when_under_concurrent_load() { var runtimeContext = TestRuntime.Create(); - var hostedService = new CapsuleBackgroundService((CapsuleHost)runtimeContext.Host); + var hostedService = new CapsuleBackgroundService( + (CapsuleHost)runtimeContext.Host, + Mock.Of>()); await hostedService.StartAsync(CancellationToken.None);