diff --git a/src/modules/Elsa.Workflows.Core/Contracts/ILoggerStateGenerator.cs b/src/modules/Elsa.Workflows.Core/Contracts/ILoggerStateGenerator.cs new file mode 100644 index 000000000..136babbcf --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Contracts/ILoggerStateGenerator.cs @@ -0,0 +1,14 @@ +namespace Elsa.Workflows.Contracts; + +/// +/// Generates logger state for the specified . +/// +public interface ILoggerStateGenerator +{ + /// + /// Generates logger state for the specified . + /// + /// The to generate logger state for. + /// A containing the state related to the . + Dictionary GenerateLoggerState(TContext context); +} diff --git a/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs index 048c657e9..5501a0b5e 100644 --- a/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs +++ b/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs @@ -18,6 +18,7 @@ using Elsa.Workflows.PortResolvers; using Elsa.Workflows.Serialization.Configurators; using Elsa.Workflows.Serialization.Helpers; using Elsa.Workflows.Serialization.Serializers; +using Elsa.Workflows.Services; using Elsa.Workflows.UIHints.CheckList; using Elsa.Workflows.UIHints.Dropdown; using Elsa.Workflows.UIHints.JsonEditor; @@ -50,6 +51,12 @@ public class WorkflowsFeature : FeatureBase /// A handler for committing workflow execution state. public Func CommitStateHandler { get; set; } = sp => new NoopCommitStateHandler(); + /// A factory that instantiates a concrete . + public Func> WorkflowLoggerStateGenerator { get; set; } = sp => new WorkflowLoggerStateGenerator(); + + /// A factory that instantiates a concrete . + public Func> ActivityLoggerStateGenerator { get; set; } = sp => new ActivityLoggerStateGenerator(); + /// A delegate to configure the . public Action WorkflowExecutionPipeline { get; set; } = builder => builder .UseExceptionHandling() @@ -58,9 +65,7 @@ public class WorkflowsFeature : FeatureBase /// A delegate to configure the . public Action ActivityExecutionPipeline { get; set; } = builder => builder.UseDefaultActivityInvoker(); - /// /// Fluent method to set . - /// public WorkflowsFeature WithStandardInStreamProvider(Func provider) { StandardInStreamProvider = provider; @@ -79,8 +84,22 @@ public class WorkflowsFeature : FeatureBase { IdentityGenerator = generator; return this; + } + + /// Fluent method to set . + public WorkflowsFeature WithWorkflowLoggerStateGenerator(Func> generator) + { + WorkflowLoggerStateGenerator = generator; + return this; } - + + /// Fluent method to set . + public WorkflowsFeature WithActivityLoggerStateGenerator(Func> generator) + { + ActivityLoggerStateGenerator = generator; + return this; + } + /// Fluent method to configure the . public WorkflowsFeature WithWorkflowExecutionPipeline(Action setup) { @@ -176,7 +195,11 @@ public class WorkflowsFeature : FeatureBase .AddScoped() .AddScoped() - // Logging + // Logger state generators + .AddSingleton(WorkflowLoggerStateGenerator) + .AddSingleton(ActivityLoggerStateGenerator) + + // Logging .AddLogging(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs b/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs index 6139b50f4..a66286462 100644 --- a/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs +++ b/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs @@ -1,20 +1,16 @@ using Elsa.Workflows.Contracts; using Elsa.Workflows.Options; +using Microsoft.Extensions.Logging; namespace Elsa.Workflows; /// -public class ActivityInvoker : IActivityInvoker +public class ActivityInvoker( + IActivityExecutionPipeline pipeline, + ILoggerStateGenerator loggerStateGenerator, + ILogger logger) + : IActivityInvoker { - private readonly IActivityExecutionPipeline _pipeline; - - /// - /// Constructor. - /// - public ActivityInvoker(IActivityExecutionPipeline pipeline) - { - _pipeline = pipeline; - } /// public async Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, ActivityInvocationOptions? options = default) @@ -42,8 +38,11 @@ public class ActivityInvoker : IActivityInvoker /// public async Task InvokeAsync(ActivityExecutionContext activityExecutionContext) - { - // Execute the activity execution pipeline. - await _pipeline.ExecuteAsync(activityExecutionContext); + { + var loggerState = loggerStateGenerator.GenerateLoggerState(activityExecutionContext); + using var loggingScope = logger.BeginScope(loggerState); + + // Execute the activity execution pipeline. + await pipeline.ExecuteAsync(activityExecutionContext); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/ActivityLoggerStateGenerator.cs b/src/modules/Elsa.Workflows.Core/Services/ActivityLoggerStateGenerator.cs new file mode 100644 index 000000000..2dbe0863f --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Services/ActivityLoggerStateGenerator.cs @@ -0,0 +1,22 @@ +using Elsa.Workflows.Contracts; + +namespace Elsa.Workflows.Services; + +/// +/// Generates logger state for the specified . +/// +public class ActivityLoggerStateGenerator : ILoggerStateGenerator +{ + /// + /// Generates logger state for the specified . + /// + /// The to generate logger state for. + /// A containing the state related to the . + public Dictionary GenerateLoggerState(ActivityExecutionContext activityExecutionContext) + { + return new Dictionary + { + ["ActivityInstanceId"] = activityExecutionContext.Id + }; + } +} diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowLoggerStateGenerator.cs b/src/modules/Elsa.Workflows.Core/Services/WorkflowLoggerStateGenerator.cs new file mode 100644 index 000000000..4f8c88200 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Services/WorkflowLoggerStateGenerator.cs @@ -0,0 +1,22 @@ +using Elsa.Workflows.Contracts; + +namespace Elsa.Workflows.Services; + +/// +/// Generates logger state for the specified . +/// +public class WorkflowLoggerStateGenerator : ILoggerStateGenerator +{ + /// + /// Generates logger state for the specified . + /// + /// The to generate logger state for. + /// A containing the state related to the . + public Dictionary GenerateLoggerState(WorkflowExecutionContext workflowExecutionContext) + { + return new Dictionary + { + ["WorkflowInstanceId"] = workflowExecutionContext.Id + }; + } +} diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs index c5d54735d..8918b387a 100644 --- a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs +++ b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs @@ -18,7 +18,8 @@ public class WorkflowRunner( IWorkflowBuilderFactory workflowBuilderFactory, IWorkflowGraphBuilder workflowGraphBuilder, IIdentityGenerator identityGenerator, - INotificationSender notificationSender, + INotificationSender notificationSender, + ILoggerStateGenerator loggerStateGenerator, ICommitStateHandler commitStateHandler, ILogger logger) : IWorkflowRunner @@ -163,16 +164,11 @@ public class WorkflowRunner( return await RunAsync(workflowExecutionContext); } - /// public async Task RunAsync(WorkflowExecutionContext workflowExecutionContext) { - var workflowInstanceId = workflowExecutionContext.Id; - var logContext = new Dictionary - { - ["WorkflowInstanceId"] = workflowInstanceId - }; - using var loggingScope = logger.BeginScope(logContext); + var loggerState = loggerStateGenerator.GenerateLoggerState(workflowExecutionContext); + using var loggingScope = logger.BeginScope(loggerState); var workflow = workflowExecutionContext.Workflow; var cancellationToken = workflowExecutionContext.CancellationToken;