Log inner exceptions

This commit is contained in:
Sipke Schoorstra 2021-02-01 16:14:45 +01:00
parent d0523439ad
commit 59cdcdad6c
8 changed files with 47 additions and 15 deletions

View file

@ -1,14 +1,21 @@
using System;
using Elsa.Services.Models;
namespace Elsa.ActivityResults
{
public class FaultResult : ActivityExecutionResult
{
public FaultResult(Exception exception) => Exception = exception;
public FaultResult(string message) => Message = message;
public string Message { get; set; }
public string? StackTrace { get; set; }
protected override void Execute(ActivityExecutionContext activityExecutionContext) =>
activityExecutionContext.WorkflowExecutionContext.Fault(activityExecutionContext.ActivityBlueprint.Id, Message, StackTrace, activityExecutionContext.Input, activityExecutionContext.Resuming);
public Exception Exception { get; } = default!;
public string Message { get; } = default!;
protected override void Execute(ActivityExecutionContext activityExecutionContext)
{
if(Exception != null!)
activityExecutionContext.WorkflowExecutionContext.Fault(Exception, activityExecutionContext.ActivityBlueprint.Id, activityExecutionContext.Input, activityExecutionContext.Resuming);
else
activityExecutionContext.WorkflowExecutionContext.Fault(Message!, activityExecutionContext.ActivityBlueprint.Id, activityExecutionContext.Input, activityExecutionContext.Resuming);
}
}
}

View file

@ -0,0 +1,20 @@
using System;
namespace Elsa.Models
{
public record SimpleException(Type Type, string Message, string StackTrace, SimpleException? InnerException = default)
{
public static SimpleException? FromException(Exception? ex)
{
if (ex == null)
return null;
var simpleException = new SimpleException(ex.GetType(), ex.Message, ex.StackTrace);
if (ex.InnerException != null)
simpleException = simpleException with { InnerException = FromException(ex.InnerException) };
return simpleException;
}
}
}

View file

@ -1,4 +1,4 @@
namespace Elsa.Models
{
public record WorkflowFault(string? FaultedActivityId, string? Message, string? StackTrace, object? ActivityInput, bool Resuming);
public record WorkflowFault(SimpleException? Exception, string Message, string? FaultedActivityId, object? ActivityInput, bool Resuming);
}

View file

@ -46,6 +46,7 @@ namespace Elsa.Services
protected ScheduleActivitiesResult Schedule(IEnumerable<ScheduledActivity> activities) => new(activities);
protected CombinedResult Combine(IEnumerable<IActivityExecutionResult> results) => new(results);
protected CombinedResult Combine(params IActivityExecutionResult[] results) => new(results);
protected FaultResult Fault(Exception exception) => new(exception);
protected FaultResult Fault(string message) => new(message);
protected T? GetState<T>([CallerMemberName] string name = null!) => Data.GetState<T>(name);

View file

@ -111,13 +111,15 @@ namespace Elsa.Services.Models
public void Begin() => WorkflowInstance.WorkflowStatus = WorkflowStatus.Running;
public void Resume() => WorkflowInstance.WorkflowStatus = WorkflowStatus.Running;
public void Suspend() => WorkflowInstance.WorkflowStatus = WorkflowStatus.Suspended;
public void Fault(string? activityId, string? message, string? stackTrace, object? activityInput, bool resuming)
public void Fault(Exception ex, string? activityId, object? activityInput, bool resuming) => Fault(ex, ex.Message, activityId, activityInput, resuming);
public void Fault(string message, string? activityId, object? activityInput, bool resuming) => Fault(null, message, activityId, activityInput, resuming);
public void Fault(Exception? exception, string message, string? activityId, object? activityInput, bool resuming)
{
var clock = ServiceProvider.GetRequiredService<IClock>();
WorkflowInstance.WorkflowStatus = WorkflowStatus.Faulted;
WorkflowInstance.FaultedAt = clock.GetCurrentInstant();
WorkflowInstance.Fault = new WorkflowFault(activityId, message, stackTrace, activityInput, resuming);
WorkflowInstance.Fault = new WorkflowFault(SimpleException.FromException(exception), message, activityId, activityInput, resuming);
}
public void Complete() => WorkflowInstance.WorkflowStatus = WorkflowStatus.Finished;

View file

@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Exceptions;
using Elsa.Models;
using Elsa.Services;
using Elsa.Services.Models;

View file

@ -176,10 +176,10 @@ namespace Elsa.Services
var loadContext = new LoadWorkflowContext(workflowExecutionContext);
workflowExecutionContext.WorkflowContext = await _workflowContextManager.LoadContext(loadContext, cancellationToken);
}
// If the workflow instance has a CurrentActivity, it means the workflow instance is being retried.
var currentActivity = workflowInstance.CurrentActivity;
if (currentActivity != null)
{
activityId = currentActivity.ActivityId;
@ -187,7 +187,7 @@ namespace Elsa.Services
}
var activity = activityId != null ? workflowBlueprint.GetActivity(activityId) : default;
// Give application a chance to prevent workflow from executing.
var validateWorkflowExecution = new ValidateWorkflowExecution(workflowExecutionContext, activity);
await _mediator.Publish(validateWorkflowExecution, cancellationToken);
@ -309,7 +309,7 @@ namespace Elsa.Services
catch (Exception e)
{
_logger.LogWarning(e, "Failed to run workflow {WorkflowInstanceId}", workflowExecutionContext.WorkflowInstance.Id);
workflowExecutionContext.Fault(null, e.Message, e.StackTrace, null, activityOperation == Resume);
workflowExecutionContext.Fault(e, null, null, activityOperation == Resume);
}
}
@ -371,7 +371,7 @@ namespace Elsa.Services
catch (Exception e)
{
_logger.LogWarning(e, "Failed to run activity {ActivityId} of workflow {WorkflowInstanceId}", activity.Id, activityExecutionContext.WorkflowInstance.Id);
activityExecutionContext.WorkflowExecutionContext.Fault(activity.Id, e.Message, e.StackTrace, activityExecutionContext.Input, activityExecutionContext.Resuming);
activityExecutionContext.WorkflowExecutionContext.Fault(e, activity.Id, activityExecutionContext.Input, activityExecutionContext.Resuming);
await _mediator.Publish(new ActivityFaulted(e, activityExecutionContext), cancellationToken);
}

View file

@ -1,4 +1,5 @@
using System;
using System.Net;
using Elsa.Activities.Console;
using Elsa.Activities.Timers;
using Elsa.Builders;
@ -13,7 +14,7 @@ namespace Elsa.Samples.Faulting.Workflows
builder
.StartIn(Duration.FromSeconds(1))
.WriteLine("Catch this!")
.Then(() => throw new ArithmeticException("Does not compute"));
.Then(() => throw new ArithmeticException("Does not compute", new ArgumentException("Incorrect argument", new ArgumentOutOfRangeException("This is the root problem", default(Exception)))));
}
}
}