Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Web API IExceptionLogger called twice

My exception logger implementing [IExceptionLogger][1] is called twice when an exception is thrown from a controller action.

context.RequestContext.Configuration.Services.GetServices(typeof(IExceptionLogger))

this returns a single entry so I'm sure only one instance of the logger is registered. I don't want my unhandled exceptions to be logged twice and I don't think that's expected default behavior. How do I troubleshoot what causes it?

If I set a breakpoint in LogAsync() method the callstack shows [External Code] on both calls.

[RoutePrefix("test")]
public class MyController : ApiController
{
    [HttpGet, AllowAnonymous]
    [Route("test")]
    public string GetTest()
    {
       throw new InvalidOperationException("Shit happens");
    }
}

The logger is injected like this:

config.Services.Add(typeof(IExceptionLogger), new WebApiExceptionLogger());

public class WebApiExceptionLogger : IExceptionLogger
{
    private readonly ILoggingService loggingService;

    public WebApiExceptionLogger()
    {
        this.loggingService = ServiceContainer.Factory.Resolve<ILoggingService>();
    }

    public Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
    {
        this.loggingService.Log(LogLevel.Error, this.GetType().Name, context.Exception, "Ship has just happened");
        return Task.FromResult(true);
    }
}
like image 253
UserControl Avatar asked Jan 26 '17 10:01

UserControl


2 Answers

Try change WebApiExceptionLogger so that it is inheriting from ExceptionLogger instead. ExceptionLogger implements a custom method called "ShouldLog" which suppresses duplicate calls. (you should NOT override this method unless you can provide similar logic). This solved the issue for me.

I think this behavior is because WebApi2 calls Log/LogAsync twice for each request when an Exception has occurred from different locations in the pipeline. Unless you have logic to handle this you will end up with duplicate log calls.

like image 131
Oskar Sjöberg Avatar answered Nov 05 '22 18:11

Oskar Sjöberg


A side note about when you'd like to resolve your global exception handlers with autofac here is what you could do:

1) Make sure autofac types registration method is called first. There you typically do something like this:

        builder.RegisterType<AppExceptionLogger>().AsSelf().AsImplementedInterfaces();
        builder.RegisterType<AppExceptionHandler>().AsSelf().AsImplementedInterfaces();

2) Add the following to your WebApiConfig.Register(HttpConfiguration config) or the similar call you have:

        // Inject our exception logger and handler (get one from DR/Autofac)
        config.Services.Replace(typeof(IExceptionHandler), config.DependencyResolver.GetService(typeof(AppExceptionHandler)));
        config.Services.Replace(typeof(IExceptionLogger), config.DependencyResolver.GetService(typeof(AppExceptionLogger)));

This works without OWIN but it should be fine there as well.

like image 23
lnaie Avatar answered Nov 05 '22 18:11

lnaie