From 07119c8ff5a2e6ecdde293a82f62ecab97ca0381 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 20 Feb 2025 20:33:55 +0100 Subject: [PATCH] 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. --- .../Abstractions/ErrorSpanHandlerBase.cs | 9 +++++++ .../Contracts/IErrorSpanHandler.cs | 10 ++++++++ .../Extensions/ModuleExtensions.cs | 14 +++++++++++ .../Handlers/DefaultErrorSpanHandler.cs | 24 +++++++++++++++++++ .../FaultExceptionErrorSpanHandler.cs | 19 +++++++++++++++ .../Models/ErrorSpanContext.cs | 9 +++++++ 6 files changed, 85 insertions(+) create mode 100644 src/modules/Elsa.OpenTelemetry/Abstractions/ErrorSpanHandlerBase.cs create mode 100644 src/modules/Elsa.OpenTelemetry/Contracts/IErrorSpanHandler.cs create mode 100644 src/modules/Elsa.OpenTelemetry/Extensions/ModuleExtensions.cs create mode 100644 src/modules/Elsa.OpenTelemetry/Handlers/DefaultErrorSpanHandler.cs create mode 100644 src/modules/Elsa.OpenTelemetry/Handlers/FaultExceptionErrorSpanHandler.cs create mode 100644 src/modules/Elsa.OpenTelemetry/Models/ErrorSpanContext.cs diff --git a/src/modules/Elsa.OpenTelemetry/Abstractions/ErrorSpanHandlerBase.cs b/src/modules/Elsa.OpenTelemetry/Abstractions/ErrorSpanHandlerBase.cs new file mode 100644 index 000000000..420936119 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Abstractions/ErrorSpanHandlerBase.cs @@ -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); +} \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/Contracts/IErrorSpanHandler.cs b/src/modules/Elsa.OpenTelemetry/Contracts/IErrorSpanHandler.cs new file mode 100644 index 000000000..d15c3dee4 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Contracts/IErrorSpanHandler.cs @@ -0,0 +1,10 @@ +using Elsa.OpenTelemetry.Abstractions; +using Elsa.OpenTelemetry.Models; + +namespace Elsa.OpenTelemetry.Contracts; + +public interface IErrorSpanHandler +{ + void Handle(ErrorSpanContext context); +} + diff --git a/src/modules/Elsa.OpenTelemetry/Extensions/ModuleExtensions.cs b/src/modules/Elsa.OpenTelemetry/Extensions/ModuleExtensions.cs new file mode 100644 index 000000000..25edd2794 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Extensions/ModuleExtensions.cs @@ -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? configure = null) + { + configuration.Configure(configure); + return configuration; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/Handlers/DefaultErrorSpanHandler.cs b/src/modules/Elsa.OpenTelemetry/Handlers/DefaultErrorSpanHandler.cs new file mode 100644 index 000000000..04bebef62 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Handlers/DefaultErrorSpanHandler.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/Handlers/FaultExceptionErrorSpanHandler.cs b/src/modules/Elsa.OpenTelemetry/Handlers/FaultExceptionErrorSpanHandler.cs new file mode 100644 index 000000000..14aa2860a --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Handlers/FaultExceptionErrorSpanHandler.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/Models/ErrorSpanContext.cs b/src/modules/Elsa.OpenTelemetry/Models/ErrorSpanContext.cs new file mode 100644 index 000000000..a364b1b68 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Models/ErrorSpanContext.cs @@ -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; +} \ No newline at end of file