Enhance logging by adding logger state to activity and workflow pipelines (#5985)
* Enhance logging by adding logger state to activity and workflow pipelines * Change logger state for ActivityExecutionContext to use key of ActivityInstanceId instead of ActivityId * Adjusted logger state registration to conform to the Elsa Features framework delegate pattern --------- Co-authored-by: Bob Hauser <rhauser@kinaxis.com> Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
This commit is contained in:
parent
368d8d9770
commit
dd711bf4ff
|
|
@ -0,0 +1,14 @@
|
|||
namespace Elsa.Workflows.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Generates logger state for the specified <typeparamref name="TContext"/>.
|
||||
/// </summary>
|
||||
public interface ILoggerStateGenerator<TContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates logger state for the specified <typeparamref name="TContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <typeparamref name="TContext"/> to generate logger state for.</param>
|
||||
/// <returns>A <see cref="Dictionary{String, Object}" /> containing the state related to the <typeparamref name="TContext"/>.</returns>
|
||||
Dictionary<string, object> GenerateLoggerState(TContext context);
|
||||
}
|
||||
|
|
@ -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<IServiceProvider, ICommitStateHandler> CommitStateHandler { get; set; } = sp => new NoopCommitStateHandler();
|
||||
|
||||
/// A factory that instantiates a concrete <see cref="ILoggerStateGenerator{WorkflowExecutionContext}"/>.
|
||||
public Func<IServiceProvider, ILoggerStateGenerator<WorkflowExecutionContext>> WorkflowLoggerStateGenerator { get; set; } = sp => new WorkflowLoggerStateGenerator();
|
||||
|
||||
/// A factory that instantiates a concrete <see cref="ILoggerStateGenerator{ActivityExecutionContext}"/>.
|
||||
public Func<IServiceProvider, ILoggerStateGenerator<ActivityExecutionContext>> ActivityLoggerStateGenerator { get; set; } = sp => new ActivityLoggerStateGenerator();
|
||||
|
||||
/// A delegate to configure the <see cref="IWorkflowExecutionPipeline"/>.
|
||||
public Action<IWorkflowExecutionPipelineBuilder> WorkflowExecutionPipeline { get; set; } = builder => builder
|
||||
.UseExceptionHandling()
|
||||
|
|
@ -58,9 +65,7 @@ public class WorkflowsFeature : FeatureBase
|
|||
/// A delegate to configure the <see cref="IActivityExecutionPipeline"/>.
|
||||
public Action<IActivityExecutionPipelineBuilder> ActivityExecutionPipeline { get; set; } = builder => builder.UseDefaultActivityInvoker();
|
||||
|
||||
/// <summary>
|
||||
/// Fluent method to set <see cref="StandardInStreamProvider"/>.
|
||||
/// </summary>
|
||||
public WorkflowsFeature WithStandardInStreamProvider(Func<IServiceProvider, IStandardInStreamProvider> provider)
|
||||
{
|
||||
StandardInStreamProvider = provider;
|
||||
|
|
@ -79,8 +84,22 @@ public class WorkflowsFeature : FeatureBase
|
|||
{
|
||||
IdentityGenerator = generator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Fluent method to set <see cref="ILoggerStateGenerator{WorkflowExecutionContext}"/>.
|
||||
public WorkflowsFeature WithWorkflowLoggerStateGenerator(Func<IServiceProvider, ILoggerStateGenerator<WorkflowExecutionContext>> generator)
|
||||
{
|
||||
WorkflowLoggerStateGenerator = generator;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// Fluent method to set <see cref="ILoggerStateGenerator{ActivityExecutionContext}"/>.
|
||||
public WorkflowsFeature WithActivityLoggerStateGenerator(Func<IServiceProvider, ILoggerStateGenerator<ActivityExecutionContext>> generator)
|
||||
{
|
||||
ActivityLoggerStateGenerator = generator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Fluent method to configure the <see cref="IWorkflowExecutionPipeline"/>.
|
||||
public WorkflowsFeature WithWorkflowExecutionPipeline(Action<IWorkflowExecutionPipelineBuilder> setup)
|
||||
{
|
||||
|
|
@ -176,7 +195,11 @@ public class WorkflowsFeature : FeatureBase
|
|||
.AddScoped<IUIHintHandler, CheckListUIHintHandler>()
|
||||
.AddScoped<IUIHintHandler, JsonEditorUIHintHandler>()
|
||||
|
||||
// Logging
|
||||
// Logger state generators
|
||||
.AddSingleton(WorkflowLoggerStateGenerator)
|
||||
.AddSingleton(ActivityLoggerStateGenerator)
|
||||
|
||||
// Logging
|
||||
.AddLogging();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,16 @@
|
|||
using Elsa.Workflows.Contracts;
|
||||
using Elsa.Workflows.Options;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Elsa.Workflows;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class ActivityInvoker : IActivityInvoker
|
||||
public class ActivityInvoker(
|
||||
IActivityExecutionPipeline pipeline,
|
||||
ILoggerStateGenerator<ActivityExecutionContext> loggerStateGenerator,
|
||||
ILogger<ActivityInvoker> logger)
|
||||
: IActivityInvoker
|
||||
{
|
||||
private readonly IActivityExecutionPipeline _pipeline;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public ActivityInvoker(IActivityExecutionPipeline pipeline)
|
||||
{
|
||||
_pipeline = pipeline;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, ActivityInvocationOptions? options = default)
|
||||
|
|
@ -42,8 +38,11 @@ public class ActivityInvoker : IActivityInvoker
|
|||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Elsa.Workflows.Contracts;
|
||||
|
||||
namespace Elsa.Workflows.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Generates logger state for the specified <see cref="ActivityExecutionContext"/>.
|
||||
/// </summary>
|
||||
public class ActivityLoggerStateGenerator : ILoggerStateGenerator<ActivityExecutionContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates logger state for the specified <see cref="ActivityExecutionContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="activityExecutionContext">The <see cref="ActivityExecutionContext"/> to generate logger state for.</param>
|
||||
/// <returns>A <see cref="Dictionary{String, Object}" /> containing the state related to the <see cref="ActivityExecutionContext"/>.</returns>
|
||||
public Dictionary<string, object> GenerateLoggerState(ActivityExecutionContext activityExecutionContext)
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
["ActivityInstanceId"] = activityExecutionContext.Id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Elsa.Workflows.Contracts;
|
||||
|
||||
namespace Elsa.Workflows.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Generates logger state for the specified <see cref="WorkflowExecutionContext"/>.
|
||||
/// </summary>
|
||||
public class WorkflowLoggerStateGenerator : ILoggerStateGenerator<WorkflowExecutionContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates logger state for the specified <see cref="WorkflowExecutionContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="workflowExecutionContext">The <see cref="WorkflowExecutionContext"/> to generate logger state for.</param>
|
||||
/// <returns>A <see cref="Dictionary{String, Object}" /> containing the state related to the <see cref="WorkflowExecutionContext"/>.</returns>
|
||||
public Dictionary<string, object> GenerateLoggerState(WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
["WorkflowInstanceId"] = workflowExecutionContext.Id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,8 @@ public class WorkflowRunner(
|
|||
IWorkflowBuilderFactory workflowBuilderFactory,
|
||||
IWorkflowGraphBuilder workflowGraphBuilder,
|
||||
IIdentityGenerator identityGenerator,
|
||||
INotificationSender notificationSender,
|
||||
INotificationSender notificationSender,
|
||||
ILoggerStateGenerator<WorkflowExecutionContext> loggerStateGenerator,
|
||||
ICommitStateHandler commitStateHandler,
|
||||
ILogger<WorkflowRunner> logger)
|
||||
: IWorkflowRunner
|
||||
|
|
@ -163,16 +164,11 @@ public class WorkflowRunner(
|
|||
return await RunAsync(workflowExecutionContext);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<RunWorkflowResult> RunAsync(WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
var workflowInstanceId = workflowExecutionContext.Id;
|
||||
var logContext = new Dictionary<string, object>
|
||||
{
|
||||
["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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue