Add OpenTelemetry error span handling infrastructure

Introduce infrastructure for handling error spans with OpenTelemetry, including `DefaultErrorSpanHandler` and `FaultExceptionErrorSpanHandler`. Define core abstractions (`ErrorSpanHandlerBase`, `IErrorSpanHandler`) and utilities for customizing OpenTelemetry integration. This enables improved error tracing and categorization in workflows.
This commit is contained in:
Sipke Schoorstra 2025-02-20 20:33:55 +01:00
parent ae676cf8e9
commit 07119c8ff5
No known key found for this signature in database
GPG key ID: 5C10502B28A4268F
6 changed files with 85 additions and 0 deletions

View file

@ -0,0 +1,9 @@
using Elsa.OpenTelemetry.Contracts;
using Elsa.OpenTelemetry.Models;
namespace Elsa.OpenTelemetry.Abstractions;
public abstract class ErrorSpanHandlerBase : IErrorSpanHandler
{
public abstract void Handle(ErrorSpanContext context);
}

View file

@ -0,0 +1,10 @@
using Elsa.OpenTelemetry.Abstractions;
using Elsa.OpenTelemetry.Models;
namespace Elsa.OpenTelemetry.Contracts;
public interface IErrorSpanHandler
{
void Handle(ErrorSpanContext context);
}

View file

@ -0,0 +1,14 @@
using Elsa.Features.Services;
using Elsa.OpenTelemetry.Features;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
public static class ModuleExtensions
{
public static IModule UseOpenTelemetry(this IModule configuration, Action<OpenTelemetryFeature>? configure = null)
{
configuration.Configure(configure);
return configuration;
}
}

View file

@ -0,0 +1,24 @@
using Elsa.OpenTelemetry.Abstractions;
using Elsa.OpenTelemetry.Models;
namespace Elsa.OpenTelemetry.Handlers;
public class DefaultErrorSpanHandler : ErrorSpanHandlerBase
{
public override void Handle(ErrorSpanContext context)
{
var span = context.Span;
var exception = context.Exception;
var errorMessage = string.IsNullOrWhiteSpace(exception?.Message) ? "Unknown error" : exception.Message;
span.SetTag("error", true);
span.SetTag("error.message", errorMessage);
if (exception != null)
{
span.SetTag("error.exceptionType", exception.GetType().FullName);
if (!string.IsNullOrEmpty(exception.StackTrace))
span.SetTag("error.stackTrace", exception.StackTrace);
}
}
}

View file

@ -0,0 +1,19 @@
using Elsa.OpenTelemetry.Abstractions;
using Elsa.OpenTelemetry.Models;
using Elsa.Workflows.Exceptions;
namespace Elsa.OpenTelemetry.Handlers;
public class FaultExceptionErrorSpanHandler : ErrorSpanHandlerBase
{
public override void Handle(ErrorSpanContext context)
{
if(context.Exception is not FaultException faultException)
return;
var span = context.Span;
span.SetTag("error.code", faultException.Code);
span.SetTag("error.category", faultException.Category);
span.SetTag("error.faultType", faultException.Type);
}
}

View file

@ -0,0 +1,9 @@
using System.Diagnostics;
namespace Elsa.OpenTelemetry.Models;
public class ErrorSpanContext(Activity span, Exception? exception)
{
public Activity Span => span;
public Exception? Exception => exception;
}