diff --git a/samples/PowerPipe.Sample/PowerPipe.Sample.csproj b/samples/PowerPipe.Sample/PowerPipe.Sample.csproj index 6ed4bad..1d37e1a 100644 --- a/samples/PowerPipe.Sample/PowerPipe.Sample.csproj +++ b/samples/PowerPipe.Sample/PowerPipe.Sample.csproj @@ -15,6 +15,8 @@ + + diff --git a/samples/PowerPipe.Sample/Program.cs b/samples/PowerPipe.Sample/Program.cs index 94c4557..d521d7e 100644 --- a/samples/PowerPipe.Sample/Program.cs +++ b/samples/PowerPipe.Sample/Program.cs @@ -2,6 +2,7 @@ using System; using System.Reflection; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using PowerPipe.Sample.Steps; namespace PowerPipe.Sample; @@ -30,6 +31,13 @@ private static IServiceProvider ConfigureServices() .AddScoped(typeof(SampleGenericStep<>)); }); + services.AddLogging(options => + { + options + .AddConsole() + .SetMinimumLevel(LogLevel.Debug); + }); + services.AddSingleton(); var serviceProvider = services.BuildServiceProvider(); diff --git a/samples/PowerPipe.Sample/Steps/SampleGenericStep.cs b/samples/PowerPipe.Sample/Steps/SampleGenericStep.cs index f786e69..1dad5b1 100644 --- a/samples/PowerPipe.Sample/Steps/SampleGenericStep.cs +++ b/samples/PowerPipe.Sample/Steps/SampleGenericStep.cs @@ -11,7 +11,6 @@ public class SampleGenericStep: IPipelineStep public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleGenericStep)} Executed"); await NextStep.ExecuteAsync(context, cancellationToken); } } diff --git a/samples/PowerPipe.Sample/Steps/SampleParallelStep1.cs b/samples/PowerPipe.Sample/Steps/SampleParallelStep1.cs index f768f74..3a5467f 100644 --- a/samples/PowerPipe.Sample/Steps/SampleParallelStep1.cs +++ b/samples/PowerPipe.Sample/Steps/SampleParallelStep1.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleParallelStep1 : IPipelineParallelStep { public ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleParallelStep1)} Executed"); return ValueTask.CompletedTask; } } diff --git a/samples/PowerPipe.Sample/Steps/SampleParallelStep2.cs b/samples/PowerPipe.Sample/Steps/SampleParallelStep2.cs index 355ad3f..15833e3 100644 --- a/samples/PowerPipe.Sample/Steps/SampleParallelStep2.cs +++ b/samples/PowerPipe.Sample/Steps/SampleParallelStep2.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleParallelStep2 : IPipelineParallelStep { public ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleParallelStep2)} Executed"); return ValueTask.CompletedTask; } } diff --git a/samples/PowerPipe.Sample/Steps/SampleParallelStep3.cs b/samples/PowerPipe.Sample/Steps/SampleParallelStep3.cs index dad6573..a7f9edc 100644 --- a/samples/PowerPipe.Sample/Steps/SampleParallelStep3.cs +++ b/samples/PowerPipe.Sample/Steps/SampleParallelStep3.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleParallelStep3 : IPipelineParallelStep { public ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleParallelStep3)} Executed"); return ValueTask.CompletedTask; } } diff --git a/samples/PowerPipe.Sample/Steps/SampleParallelStep4.cs b/samples/PowerPipe.Sample/Steps/SampleParallelStep4.cs index c9a3ee0..e0a443f 100644 --- a/samples/PowerPipe.Sample/Steps/SampleParallelStep4.cs +++ b/samples/PowerPipe.Sample/Steps/SampleParallelStep4.cs @@ -9,8 +9,6 @@ public class SampleParallelStep4 : IPipelineParallelStep { public ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleParallelStep4)} Executed"); - throw new InvalidOperationException(); // return ValueTask.CompletedTask; diff --git a/samples/PowerPipe.Sample/Steps/SampleParallelStep5.cs b/samples/PowerPipe.Sample/Steps/SampleParallelStep5.cs index 54f7aae..edd51f6 100644 --- a/samples/PowerPipe.Sample/Steps/SampleParallelStep5.cs +++ b/samples/PowerPipe.Sample/Steps/SampleParallelStep5.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,8 +8,6 @@ public class SampleParallelStep5 : IPipelineParallelStep { public ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleParallelStep5)} Executed"); - // throw new InvalidOperationException(); return ValueTask.CompletedTask; diff --git a/samples/PowerPipe.Sample/Steps/SampleParallelStep5Compensation.cs b/samples/PowerPipe.Sample/Steps/SampleParallelStep5Compensation.cs index ff8f286..c4b9141 100644 --- a/samples/PowerPipe.Sample/Steps/SampleParallelStep5Compensation.cs +++ b/samples/PowerPipe.Sample/Steps/SampleParallelStep5Compensation.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleParallelStep5Compensation : IPipelineCompensationStep { public ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleParallelStep6)} Executed"); return ValueTask.CompletedTask; } } diff --git a/samples/PowerPipe.Sample/Steps/SampleParallelStep7.cs b/samples/PowerPipe.Sample/Steps/SampleParallelStep7.cs index 1534b6d..25e142f 100644 --- a/samples/PowerPipe.Sample/Steps/SampleParallelStep7.cs +++ b/samples/PowerPipe.Sample/Steps/SampleParallelStep7.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleParallelStep7 : IPipelineParallelStep { public ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleParallelStep7)} Executed"); return ValueTask.CompletedTask; } } diff --git a/samples/PowerPipe.Sample/Steps/SampleStep1.cs b/samples/PowerPipe.Sample/Steps/SampleStep1.cs index f9ba88a..75df497 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep1.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep1.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -10,7 +9,6 @@ public class SampleStep1 : IPipelineStep public IPipelineStep NextStep { get; set; } public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleStep1)} Executed"); await NextStep.ExecuteAsync(context, cancellationToken); } } diff --git a/samples/PowerPipe.Sample/Steps/SampleStep1Compensation.cs b/samples/PowerPipe.Sample/Steps/SampleStep1Compensation.cs index e08c484..f4ab175 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep1Compensation.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep1Compensation.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleStep1Compensation : IPipelineCompensationStep public IPipelineStep NextStep { get; set; } public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleStep2)} Executed"); await NextStep.ExecuteAsync(context, cancellationToken); } } diff --git a/samples/PowerPipe.Sample/Steps/SampleStep2Compensation.cs b/samples/PowerPipe.Sample/Steps/SampleStep2Compensation.cs index 8b5ad49..1eb08c6 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep2Compensation.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep2Compensation.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleStep2Compensation : IPipelineCompensationStep public IPipelineStep NextStep { get; set; } public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleStep3)} Executed"); await NextStep.ExecuteAsync(context, cancellationToken); } } diff --git a/samples/PowerPipe.Sample/Steps/SampleStep4.cs b/samples/PowerPipe.Sample/Steps/SampleStep4.cs index bf90f4d..e8335f6 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep4.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep4.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -10,7 +9,6 @@ public class SampleStep4 : IPipelineStep public IPipelineStep NextStep { get; set; } public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleStep4)} Executed"); await NextStep.ExecuteAsync(context, cancellationToken); } } diff --git a/samples/PowerPipe.Sample/Steps/SampleStep5.cs b/samples/PowerPipe.Sample/Steps/SampleStep5.cs index 1cb2352..c4f4552 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep5.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep5.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -10,7 +9,6 @@ public class SampleStep5 : IPipelineStep public IPipelineStep NextStep { get; set; } public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleStep5)} Executed"); await NextStep.ExecuteAsync(context, cancellationToken); } } diff --git a/samples/PowerPipe.Sample/Steps/SampleStep6.cs b/samples/PowerPipe.Sample/Steps/SampleStep6.cs index 68c8993..181ffe0 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep6.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep6.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -10,7 +9,6 @@ public class SampleStep6 : IPipelineStep public IPipelineStep NextStep { get; set; } public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleStep6)} Executed"); await NextStep.ExecuteAsync(context, cancellationToken); } } diff --git a/samples/PowerPipe.Sample/Steps/SampleStep7.cs b/samples/PowerPipe.Sample/Steps/SampleStep7.cs index 7ef260f..fc3b8cf 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep7.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep7.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -10,8 +9,6 @@ public class SampleStep7 : IPipelineStep public IPipelineStep NextStep { get; set; } public async ValueTask ExecuteAsync(SamplePipelineContext context, CancellationToken cancellationToken) { - Console.WriteLine($"{nameof(SampleStep7)} Executed"); - // throw new InvalidOperationException(); await NextStep.ExecuteAsync(context, cancellationToken); diff --git a/samples/PowerPipe.Sample/Steps/SampleStep7Compensation.cs b/samples/PowerPipe.Sample/Steps/SampleStep7Compensation.cs index c5e8594..be9ac7d 100644 --- a/samples/PowerPipe.Sample/Steps/SampleStep7Compensation.cs +++ b/samples/PowerPipe.Sample/Steps/SampleStep7Compensation.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using PowerPipe.Interfaces; @@ -9,7 +8,6 @@ public class SampleStep7Compensation : IPipelineCompensationStep - - - - - - - + + + + + + + diff --git a/src/PowerPipe/Builder/PipelineBuilder.cs b/src/PowerPipe/Builder/PipelineBuilder.cs index ccb04c7..c2163e9 100644 --- a/src/PowerPipe/Builder/PipelineBuilder.cs +++ b/src/PowerPipe/Builder/PipelineBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Microsoft.Extensions.Logging; using PowerPipe.Builder.Steps; using PowerPipe.Interfaces; @@ -15,6 +16,7 @@ public sealed class PipelineBuilder where TResult : class { private readonly IPipelineStepFactory _pipelineStepFactory; + private readonly ILoggerFactory _loggerFactory; private volatile TContext _context; internal List> Steps { get; } = new(); @@ -32,6 +34,7 @@ public PipelineBuilder(IPipelineStepFactory pipelineStepFactory, TContext contex _pipelineStepFactory = pipelineStepFactory; _context = context; + _loggerFactory = pipelineStepFactory.ServiceProvider.GetService(typeof(ILoggerFactory)) as ILoggerFactory; } /// @@ -42,7 +45,7 @@ public PipelineBuilder(IPipelineStepFactory pipelineStepFactory, TContext contex public PipelineBuilder Add() where T : IStepBase { - Steps.Add(new LazyStep(_pipelineStepFactory.Create)); + Steps.Add(new LazyStep(_pipelineStepFactory.Create, _loggerFactory)); return this; } @@ -56,7 +59,9 @@ public PipelineBuilder Add() public PipelineBuilder AddIf(Predicate predicate) where T : IStepBase { - Steps.Add(new AddIfStep(predicate, new LazyStep(_pipelineStepFactory.Create))); + Steps.Add(new AddIfStep( + predicate, + new LazyStep(_pipelineStepFactory.Create, _loggerFactory))); return this; } @@ -74,8 +79,8 @@ public PipelineBuilder AddIfElse(Predicate( predicate, - new LazyStep(_pipelineStepFactory.Create), - new LazyStep(_pipelineStepFactory.Create))); + new LazyStep(_pipelineStepFactory.Create, _loggerFactory), + new LazyStep(_pipelineStepFactory.Create, _loggerFactory))); return this; } @@ -151,4 +156,4 @@ public IPipeline Build() return new Pipeline(_context, Steps); } -} \ No newline at end of file +} diff --git a/src/PowerPipe/Builder/Steps/InternalStep.cs b/src/PowerPipe/Builder/Steps/InternalStep.cs index 97cba3a..2f0d1cb 100644 --- a/src/PowerPipe/Builder/Steps/InternalStep.cs +++ b/src/PowerPipe/Builder/Steps/InternalStep.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using PowerPipe.Exceptions; using PowerPipe.Interfaces; @@ -20,6 +21,11 @@ internal abstract class InternalStep : IPipelineStep, IPipel /// public CompensationStep CompensationStep { get; set; } + /// + /// Gets or init a logger. + /// + protected ILogger Logger { get; set; } + /// /// Gets or sets a value indicating whether this step has been executed. /// @@ -89,6 +95,8 @@ public async ValueTask ExecuteAsync(TContext context, CancellationToken cancella throw; } + Logger?.LogDebug("Exception thrown during execution. {exception}", e); + ErrorHandledSucceed = await HandleExceptionAsync(context, cancellationToken); if (!ErrorHandledSucceed.Value) @@ -97,7 +105,10 @@ public async ValueTask ExecuteAsync(TContext context, CancellationToken cancella finally { if (AllowedToCompensate) + { + Logger?.LogDebug("Exception handling failed, compensation executed"); await CompensationStep.CompensateAsync(context, cancellationToken); + } } } @@ -118,25 +129,38 @@ protected virtual ValueTask ExecuteInternalAsync(TContext context, CancellationT /// A task representing the asynchronous operation. Returns true if the exception is handled; otherwise, false. protected virtual async ValueTask HandleExceptionAsync(TContext context, CancellationToken cancellationToken) { + Logger?.LogDebug("Exception handling executed"); + if (ErrorHandlingPredicate is not null && !ErrorHandlingPredicate(context)) + { + Logger?.LogDebug("Exception not handled due to error handling predicate"); return false; + } switch (ErrorHandlingBehaviour) { case PipelineStepErrorHandling.Suppress: + Logger?.LogDebug("Exception suppressed"); return true; case PipelineStepErrorHandling.Retry: break; case null: + Logger?.LogDebug("Exception not handled due to absence of handling behaviour"); return false; } if (RetryCount >= (MaxRetryCount ?? 1)) + { + Logger?.LogDebug("Exception retry count exceeded"); return false; + } RetryCount++; + + Logger?.LogDebug("Step execution retry after delay, {count}", RetryCount); + await Task.Delay(RetryInterval ?? TimeSpan.FromSeconds(1), cancellationToken); await ExecuteAsync(context, cancellationToken); diff --git a/src/PowerPipe/Builder/Steps/LazyStep.cs b/src/PowerPipe/Builder/Steps/LazyStep.cs index 787daed..ffae4dc 100644 --- a/src/PowerPipe/Builder/Steps/LazyStep.cs +++ b/src/PowerPipe/Builder/Steps/LazyStep.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using PowerPipe.Interfaces; namespace PowerPipe.Builder.Steps; @@ -17,7 +18,8 @@ internal class LazyStep : InternalStep /// Initializes a new instance of the class. /// /// A factory function to create the step when needed. - internal LazyStep(Func> factory) + /// A logger factory + internal LazyStep(Func> factory, ILoggerFactory loggerFactory) { _step = new Lazy>(() => { @@ -26,6 +28,8 @@ internal LazyStep(Func> factory) if (instance is IPipelineStep step) step.NextStep = NextStep; + Logger = loggerFactory?.CreateLogger(instance.GetType()); + return instance; }); } @@ -41,5 +45,7 @@ protected override async ValueTask ExecuteInternalAsync(TContext context, Cancel StepExecuted = true; await _step.Value.ExecuteAsync(context, cancellationToken); + + Logger?.LogDebug("{Step} executed", _step.Value.GetType().FullName); } } diff --git a/src/PowerPipe/Factories/PipelineStepFactory.cs b/src/PowerPipe/Factories/PipelineStepFactory.cs index 0e8b20b..7d3b249 100644 --- a/src/PowerPipe/Factories/PipelineStepFactory.cs +++ b/src/PowerPipe/Factories/PipelineStepFactory.cs @@ -3,12 +3,11 @@ namespace PowerPipe.Factories; -/// -/// Represents a factory for creating pipeline steps and compensation steps. -/// +/// public class PipelineStepFactory : IPipelineStepFactory { - private readonly IServiceProvider _serviceProvider; + /// + public IServiceProvider ServiceProvider { get; } /// /// Initializes a new instance of the class. @@ -16,30 +15,20 @@ public class PipelineStepFactory : IPipelineStepFactory /// The service provider to resolve step instances. public PipelineStepFactory(IServiceProvider serviceProvider) { - _serviceProvider = serviceProvider; + ServiceProvider = serviceProvider; } - /// - /// Creates an instance of a pipeline step. - /// - /// The type of step to create. - /// The type of context used in the pipeline. - /// An instance of the specified pipeline step. + /// public IStepBase Create() where TStep : IStepBase { - return _serviceProvider.GetService(typeof(TStep)) as IStepBase; + return ServiceProvider.GetService(typeof(TStep)) as IStepBase; } - /// - /// Creates an instance of a pipeline compensation step. - /// - /// The type of compensation step to create. - /// The type of context used in the pipeline. - /// An instance of the specified pipeline compensation step. + /// public IPipelineCompensationStep CreateCompensation() where TStep : IPipelineCompensationStep { - return _serviceProvider.GetService(typeof(TStep)) as IPipelineCompensationStep; + return ServiceProvider.GetService(typeof(TStep)) as IPipelineCompensationStep; } -} \ No newline at end of file +} diff --git a/src/PowerPipe/Interfaces/IPipelineStepFactory.cs b/src/PowerPipe/Interfaces/IPipelineStepFactory.cs index edc4fac..1f8cba6 100644 --- a/src/PowerPipe/Interfaces/IPipelineStepFactory.cs +++ b/src/PowerPipe/Interfaces/IPipelineStepFactory.cs @@ -1,10 +1,17 @@ -namespace PowerPipe.Interfaces; +using System; + +namespace PowerPipe.Interfaces; /// /// Represents a factory for creating pipeline steps and compensation steps. /// public interface IPipelineStepFactory { + /// + /// Instance of the service provider + /// + IServiceProvider ServiceProvider { get; } + /// /// Creates an instance of a pipeline step. /// @@ -22,4 +29,4 @@ IStepBase Create() /// An instance of the specified pipeline compensation step. IPipelineCompensationStep CreateCompensation() where TStep : IPipelineCompensationStep; -} \ No newline at end of file +} diff --git a/src/PowerPipe/PowerPipe.csproj b/src/PowerPipe/PowerPipe.csproj index d607836..2c7b5f3 100644 --- a/src/PowerPipe/PowerPipe.csproj +++ b/src/PowerPipe/PowerPipe.csproj @@ -8,4 +8,16 @@ + + + + + + + + + + + +