elsa-core/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs
Sipke Schoorstra 01d286b386
Refactor serialization tests and remove unused dependencies
Updated object initialization in serialization tests for consistency and renamed `async void` methods to `async Task`. Removed unused `ISystemClock` dependency from OpenTelemetry middleware constructors to simplify implementation.
2025-05-21 10:18:17 +02:00

85 lines
3.3 KiB
C#

using System.Diagnostics;
using Elsa.Common;
using Elsa.Extensions;
using Elsa.OpenTelemetry.Contracts;
using Elsa.OpenTelemetry.Helpers;
using Elsa.OpenTelemetry.Models;
using Elsa.Workflows;
using Elsa.Workflows.Pipelines.ActivityExecution;
using JetBrains.Annotations;
using Activity = System.Diagnostics.Activity;
using ActivityKind = System.Diagnostics.ActivityKind;
namespace Elsa.OpenTelemetry.Middleware;
/// <inheritdoc />
[UsedImplicitly]
public class OpenTelemetryTracingActivityExecutionMiddleware(ActivityMiddlewareDelegate next) : IActivityExecutionMiddleware
{
/// <inheritdoc />
public async ValueTask InvokeAsync(ActivityExecutionContext context)
{
var activity = context.Activity;
using var span = ElsaOpenTelemetry.ActivitySource.StartActivity($"execute activity {activity.Type}", ActivityKind.Internal, Activity.Current?.Context ?? default);
if (span == null)
{
await next(context);
return;
}
span.SetTag("operation.name", "elsa.activity.execution");
span.SetTag("activity.id", activity.NodeId);
span.SetTag("activity.node.id", activity.NodeId);
span.SetTag("activity.type", activity.Type);
span.SetTag("activity.name", activity.Name);
span.SetTag("activity.version", activity.Version);
span.SetTag("activity.instance.id", context.Id);
span.SetTag("activity.tenant.id", context.WorkflowExecutionContext.Workflow.Identity.TenantId);
var activityKind = context.ActivityDescriptor.Kind;
if (activityKind == Elsa.Workflows.ActivityKind.Job || (activityKind == Workflows.ActivityKind.Task && activity.GetRunAsynchronously()))
span.SetTag("span.type", "job");
span.AddEvent(new("executing"));
await next(context);
if (context.Status == ActivityStatus.Faulted)
{
span.AddEvent(new("faulted"));
span.SetStatus(ActivityStatusCode.Error);
var errorSpanHandlerContext = new ActivityErrorSpanContext(span, context.Exception);
var errorSpanHandler = context.GetServices<IActivityErrorSpanHandler>()
.OrderBy(x => x.Order)
.FirstOrDefault(x => x.CanHandle(errorSpanHandlerContext));
errorSpanHandler?.Handle(errorSpanHandlerContext);
}
else if (context.Status == ActivityStatus.Canceled)
{
span.AddEvent(new("canceled"));
}
else if (context.Status == ActivityStatus.Completed)
{
span.AddEvent(new("completed"));
}
else if (context.Status == ActivityStatus.Pending)
{
span.AddEvent(new("pending"));
}
}
}
/// <summary>
/// Contains extension methods for <see cref="OpenTelemetryTracingActivityExecutionMiddleware"/>.
/// </summary>
[UsedImplicitly]
public static class OpenTelemetryTracingActivityExecutionMiddlewareExtensions
{
/// <summary>
/// Installs the <see cref="OpenTelemetryTracingActivityExecutionMiddleware"/> component in the workflow execution pipeline.
/// </summary>
public static IActivityExecutionPipelineBuilder UseActivityExecutionTracing(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.Insert<OpenTelemetryTracingActivityExecutionMiddleware>(0);
}