rebus

Rebus firstlevel retry. Is there any way to get exception from previous delivery attempt?


I know, that rebus stores errors across message deliveries in IErrorTraker (InMemErrorTracker). Is there any way to inject IErrorTraker to handlers or another way to get exception from previous attempt? Thanks!


Solution

  • Yes there is, but you'll need to do some work yourself.

    As you have correctly figured out, IErrorTracker is what Rebus uses to keep track of failed delivery attempts, and so it would be able to provide information about previously recorded exceptions, given the ID if the message being handled.

    It's internal to Rebus though, so it's not exposed in any way. You can get it out in a few different ways though, e.g. by having an incoming pipeline step put it into the incoming step context:

    The way to do that (in a crude an unsophisticated way) can be seen here:

    Configure.With(activator)
        .Transport(t => (...))
        .Options(o => o.Decorate<IPipeline>(c =>
        {
            var pipeline = c.Get<IPipeline>();
            var errorTracker = c.Get<IErrorTracker>();
            var step = new ExposeErrorTrackerStep(errorTracker);
    
            return new PipelineStepConcatenator(pipeline)
                .OnReceive(step, PipelineAbsolutePosition.Front);
        }))
        .Start();
    
    

    where the more sophisticated version would put the ugly configuration stuff into an appropriate extension method:

    public static void ExposeErrorTracker(this OptionsConfigurer configurer) =>
        configurer.Decorate<IPipeline>(c =>
        {
            var pipeline = c.Get<IPipeline>();
            var errorTracker = c.Get<IErrorTracker>();
            var step = new ExposeErrorTrackerStep(errorTracker);
    
            return new PipelineStepConcatenator(pipeline)
                .OnReceive(step, PipelineAbsolutePosition.Front);
        });
    
    

    and then make use of it like so:

    Configure.With(activator)
        .Transport(t => (...))
        .Options(o => o.ExposeErrorTracker())
        .Start();
    
    

    Finally, here's the implementation of the step:

    [StepDocumentation("Makes Rebus' IErrorTracker available in the incoming step context.")]
    class ExposeErrorTrackerStep : IIncomingStep
    {
        readonly IErrorTracker _errorTracker;
    
        public ExposeErrorTrackerStep(IErrorTracker errorTracker) => _errorTracker = errorTracker;
    
        public Task Process(IncomingStepContext context, Func<Task> next)
        {
            context.Save(_errorTracker);
            return next();
        }
    }
    
    

    As you can see, it simple stashed the IErrorTracker in the incoming step context, which you can access via IMessageContext, which is automatically configured to be injected into handlers.

    The full working example can be seen here: https://github.com/rebus-org/Rebus/blob/master/Rebus.Tests/Examples/ExposeInternalService_ErrorTracker.cs