2024-09-23 09:35:43 +00:00
|
|
|
using System.Diagnostics;
|
2024-09-23 21:35:30 +00:00
|
|
|
using System.Text.Json;
|
|
|
|
|
using Elsa.Common.Contracts;
|
2024-09-23 22:27:51 +00:00
|
|
|
using Elsa.Expressions.Services;
|
|
|
|
|
using Elsa.Extensions;
|
2024-09-23 09:35:43 +00:00
|
|
|
using Elsa.OpenTelemetry.Helpers;
|
|
|
|
|
using Elsa.Workflows;
|
|
|
|
|
using Elsa.Workflows.Contracts;
|
|
|
|
|
using Elsa.Workflows.Pipelines.WorkflowExecution;
|
2024-09-23 22:27:51 +00:00
|
|
|
using Elsa.Workflows.Serialization.Converters;
|
2024-09-23 09:35:43 +00:00
|
|
|
using JetBrains.Annotations;
|
|
|
|
|
using Activity = System.Diagnostics.Activity;
|
|
|
|
|
using ActivityKind = System.Diagnostics.ActivityKind;
|
|
|
|
|
|
|
|
|
|
namespace Elsa.OpenTelemetry.Middleware;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Middleware that traces workflow execution using OpenTelemetry.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[UsedImplicitly]
|
2024-09-23 21:35:30 +00:00
|
|
|
public class OpenTelemetryTracingWorkflowExecutionMiddleware(WorkflowMiddlewareDelegate next, ISystemClock systemClock) : WorkflowExecutionMiddleware(next)
|
2024-09-23 09:35:43 +00:00
|
|
|
{
|
2024-09-23 22:27:51 +00:00
|
|
|
private readonly JsonSerializerOptions? _incidentSerializerOptions = new JsonSerializerOptions().WithConverters(new TypeJsonConverter(WellKnownTypeRegistry.CreateDefault()));
|
|
|
|
|
|
2024-09-23 09:35:43 +00:00
|
|
|
/// <inheritdoc />
|
|
|
|
|
public override async ValueTask InvokeAsync(WorkflowExecutionContext context)
|
|
|
|
|
{
|
|
|
|
|
var workflowInstanceId = context.Id;
|
|
|
|
|
var workflow = context.Workflow;
|
2024-10-05 11:48:59 +00:00
|
|
|
using var span = ElsaOpenTelemetry.ActivitySource.StartActivity("WorkflowExecution", ActivityKind.Internal, Activity.Current?.Context ?? default);
|
2024-09-23 21:35:30 +00:00
|
|
|
|
2024-10-05 11:48:59 +00:00
|
|
|
if (span == null) // No listener is registered.
|
2024-09-23 21:35:30 +00:00
|
|
|
{
|
|
|
|
|
await Next(context);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-05 11:48:59 +00:00
|
|
|
span.SetTag("workflowInstance.id", workflowInstanceId);
|
|
|
|
|
span.SetTag("workflowDefinition.definitionId", workflow.Identity.DefinitionId);
|
|
|
|
|
span.SetTag("workflowDefinition.version", workflow.Identity.Version);
|
|
|
|
|
span.SetTag("workflowDefinition.name", workflow.WorkflowMetadata.Name);
|
|
|
|
|
span.SetTag("workflowExecution.startTimeUtc", span.StartTimeUtc);
|
|
|
|
|
span.AddEvent(new ActivityEvent("Executing", tags: CreateStatusTags(context)));
|
2024-09-23 21:35:30 +00:00
|
|
|
await Next(context);
|
|
|
|
|
|
|
|
|
|
if (context.SubStatus == WorkflowSubStatus.Faulted)
|
|
|
|
|
{
|
2024-10-05 11:48:59 +00:00
|
|
|
span.AddEvent(new ActivityEvent("Faulted", tags: CreateStatusTags(context)));
|
|
|
|
|
span.SetStatus(ActivityStatusCode.Error);
|
|
|
|
|
span.SetTag("error", true);
|
2024-09-23 21:35:30 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-10-05 11:48:59 +00:00
|
|
|
span.AddEvent(new ActivityEvent("Executed", tags: CreateStatusTags(context)));
|
|
|
|
|
span.SetStatus(ActivityStatusCode.Ok);
|
2024-09-23 21:35:30 +00:00
|
|
|
}
|
2024-09-23 09:35:43 +00:00
|
|
|
|
2024-10-05 09:19:02 +00:00
|
|
|
if(context.Incidents.Any())
|
|
|
|
|
{
|
2024-10-05 11:48:59 +00:00
|
|
|
span.SetStatus(ActivityStatusCode.Error);
|
2024-10-05 12:12:00 +00:00
|
|
|
span.SetTag("workflowInstance.hasIncidents", true);
|
2024-10-05 11:48:59 +00:00
|
|
|
span.SetTag("error", true);
|
2024-10-05 09:19:02 +00:00
|
|
|
|
|
|
|
|
if (context.Incidents.Count > 0)
|
2024-10-05 11:48:59 +00:00
|
|
|
span.SetTag("error.message", JsonSerializer.Serialize(context.Incidents, _incidentSerializerOptions));
|
2024-10-05 09:19:02 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-23 21:35:30 +00:00
|
|
|
if (!string.IsNullOrWhiteSpace(context.CorrelationId))
|
2024-10-05 12:12:00 +00:00
|
|
|
span.SetTag("workflowInstance.correlationId", context.CorrelationId);
|
2024-09-23 09:35:43 +00:00
|
|
|
|
2024-10-05 11:48:59 +00:00
|
|
|
var now = systemClock.UtcNow;
|
|
|
|
|
span.SetTag("workflowExecution.endTimeUtc", now);
|
|
|
|
|
span.SetTag("workflowExecution.durationMs", (now - span.StartTimeUtc).TotalMilliseconds);
|
2024-09-23 09:35:43 +00:00
|
|
|
}
|
2024-10-05 11:39:58 +00:00
|
|
|
|
|
|
|
|
private ActivityTagsCollection CreateStatusTags(WorkflowExecutionContext context)
|
|
|
|
|
{
|
|
|
|
|
return new ActivityTagsCollection(new Dictionary<string, object?>
|
|
|
|
|
{
|
|
|
|
|
["workflowInstance.status"] = context.Status.ToString(),
|
|
|
|
|
["workflowInstance.subStatus"] = context.SubStatus.ToString()
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-09-23 09:35:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains extension methods for <see cref="OpenTelemetryTracingWorkflowExecutionMiddleware"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[UsedImplicitly]
|
|
|
|
|
public static class OpenTelemetryWorkflowExecutionMiddlewareExtensions
|
|
|
|
|
{
|
|
|
|
|
/// Installs the <see cref="OpenTelemetryTracingWorkflowExecutionMiddleware"/> component in the workflow execution pipeline.
|
|
|
|
|
public static IWorkflowExecutionPipelineBuilder UseWorkflowExecutionTracing(this IWorkflowExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.Insert<OpenTelemetryTracingWorkflowExecutionMiddleware>(0);
|
2024-09-23 21:35:30 +00:00
|
|
|
}
|