Rebus 7 IErrorHandler implementation not compatible with Rebus 8

31 views Asked by At

I have this Rebus 7 IErrorHandler implementation to report failed messages on Slack:

public class ReportFailedMessageErrorHandler(
        IErrorHandler errorHandler,
        ISerializer serializer,
        SlackNotificationSender slackNotificationSender,
        IOptions<RuntimeOptions> runtimeOptions,
        LinkGenerator linkGenerator
    )
    : IErrorHandler
{
    public async Task HandlePoisonMessage(
        TransportMessage transportMessage, 
        ITransactionContext transactionContext,
        Exception exception
    )
    {
        await errorHandler.HandlePoisonMessage(transportMessage, transactionContext, exception);

        var rebusMessage = await serializer.Deserialize(transportMessage);
        
        var message = rebusMessage.Body as BaseMessage;
        Guard.Hope(message != null, nameof(message) + " is null");

        var shouldNotifyOnFail = message.GetType().GetCustomAttribute<NotifyOnFailAttribute>() != null;
        if (shouldNotifyOnFail && exception.InnerException is not IgnoreNotificationException)
        {
            var jobGuid = message switch
            {
                Command command => command.Guid,
                Event => Guid.Parse(rebusMessage.Headers[Headers.MessageId]),
                _ => throw new NotSupportedException($"Unsupported BaseMessage type {message.GetType().FullName}")
            };
            var failedMessageTypeName = message.GetType().Name;
            await slackNotificationSender.SendNotification(
                $"{failedMessageTypeName} failed",
                $"{_GetSlackJobLink(jobGuid, failedMessageTypeName)} failed on {runtimeOptions.Value.Environment}" +
                $"{(exception.InnerException is UserException ? $": {exception.InnerException.Message}" : ".")}",
                SlackNotificationSenderIconEmojiConstants.Warning,
                exception.InnerException is UserException
                    ? SlackNotificationSenderChannel.ContentChannel
                    : SlackNotificationSenderChannel.DevelopmentChannel
            );
        }
    }

    private string _GetSlackJobLink(Guid jobGuid, string messageTypeName)
    {
        return $"<{runtimeOptions.Value.Url.TrimEnd('/')}{_GetJobUrl(jobGuid)}|{messageTypeName}>";
    }

    private string _GetJobUrl(Guid jobGuid)
    {
        ...
    }
}

It uses Exception.InnerException to detect the exception thrown in the message handler, and behave differently based on the exception type.

In Rebus 8, Exception has been changed to ExceptionInfo without access to InnerException. I refactored the code to this:

    public async Task HandlePoisonMessage(
        TransportMessage transportMessage, 
        ITransactionContext transactionContext,
        ExceptionInfo exception
    )
    {
        await errorHandler.HandlePoisonMessage(transportMessage, transactionContext, exception);

        var rebusMessage = await serializer.Deserialize(transportMessage);
        
        var message = rebusMessage.Body as BaseMessage;
        Guard.Hope(message != null, nameof(message) + " is null");

        var shouldNotifyOnFail = message.GetType().GetCustomAttribute<NotifyOnFailAttribute>() != null;
        if (shouldNotifyOnFail && !exception.Details.Contains(nameof(IgnoreNotificationException)))
        {
            var jobGuid = message switch
            {
                Command command => command.Guid,
                Event => Guid.Parse(rebusMessage.Headers[Headers.MessageId]),
                _ => throw new NotSupportedException($"Unsupported BaseMessage type {message.GetType().FullName}")
            };
            var failedMessageTypeName = message.GetType().Name;
            await slackNotificationSender.SendNotification(
                $"{failedMessageTypeName} failed",
                $"{_GetSlackJobLink(jobGuid, failedMessageTypeName)} failed on {runtimeOptions.Value.Environment}" +
                $"{(exception.Details.Contains(nameof(UserException)) ? $": {exception.Message}" : ".")}",
                SlackNotificationSenderIconEmojiConstants.Warning,
                exception.Details.Contains(nameof(UserException))
                    ? SlackNotificationSenderChannel.ContentChannel
                    : SlackNotificationSenderChannel.DevelopmentChannel
            );
        }
    }

Not ideal in my opinion that the InnerException disappeared, and it's now doing a stringology (exception.Details.Contains(nameof(UserException))). Is there a better way in Rebus how to implement failed message reporting, and get access to the thrown inner exception message?

0

There are 0 answers