using System.Diagnostics; using Elsa.Common.Contracts; using Elsa.OpenTelemetry.Helpers; using Elsa.Workflows; using Elsa.Workflows.Contracts; using Elsa.Workflows.Pipelines.ActivityExecution; using Elsa.Workflows.Pipelines.WorkflowExecution; using JetBrains.Annotations; using Newtonsoft.Json; using Activity = System.Diagnostics.Activity; using ActivityKind = System.Diagnostics.ActivityKind; namespace Elsa.OpenTelemetry.Middleware; /// [UsedImplicitly] public class OpenTelemetryTracingActivityExecutionMiddleware(ActivityMiddlewareDelegate next, ISystemClock systemClock) : IActivityExecutionMiddleware { /// public async ValueTask InvokeAsync(ActivityExecutionContext context) { var activity = context.Activity; using var span = ElsaOpenTelemetry.ActivitySource.StartActivity($"ActivityExecution", ActivityKind.Internal, Activity.Current?.Context ?? default); if (span == null) { await next(context); return; } span.SetTag("activity.nodeId", activity.NodeId); span.SetTag("activity.type", activity.Type); span.SetTag("activity.name", activity.Name); span.SetTag("activityInstance.id", context.Id); span.AddEvent(new ActivityEvent("Executing", tags: new ActivityTagsCollection(new Dictionary { ["activityInstance.status"] = context.Status.ToString(), }))); await next(context); if (context.Status == ActivityStatus.Faulted) { span.AddEvent(new ActivityEvent("Faulted")); span.SetStatus(ActivityStatusCode.Error); span.SetTag("error", true); span.SetTag("hasIncidents", true); var errorMessage = string.IsNullOrWhiteSpace(context.Exception?.Message) ? "Unknown error" : context.Exception.Message; span.SetTag("error.message", errorMessage); if (!string.IsNullOrEmpty(context.Exception?.StackTrace)) span.SetTag("error.stackTrace", context.Exception.StackTrace); } else span.AddEvent(new ActivityEvent("Executed", tags: new ActivityTagsCollection(new Dictionary { ["activityInstance.status"] = context.Status.ToString(), }))); span.SetTag("activityExecution.durationMs", (systemClock.UtcNow - span.StartTimeUtc).TotalMilliseconds); } } /// /// Contains extension methods for . /// [UsedImplicitly] public static class OpenTelemetryTracingActivityExecutionMiddlewareExtensions { /// Installs the component in the workflow execution pipeline. public static IActivityExecutionPipelineBuilder UseActivityExecutionTracing(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.Insert(0); }