Your official information source from the .NET Web Development and Tools group at Microsoft.
I'm a fan of using ELMAH to track unhandled exceptions in my ASP.NET applications. If you haven't tried ELMAH, you should definitely check it out. There's even an ELMAH NuGet package so it's trivial to install.
Now that I'm getting my feet wet with Web API, I'd like to have any Web API unhandled exceptions be directed to ELMAH as well so that I have a one stop shop for viewing all unhandled exceptions in my application. Without a bit of work though, this won't happen.
ELMAH records unhandled exceptions by tapping into the Application_Error event of ASP.NET applications, but when a Web API endpoint throws an unhandled exception, this event isn't triggered because the exception is intentionally swallowed higher in the Web API call stack. This enables Web API to send a response to the client like "500 - Internal Server Error". If the exception went unhandled, Web API wouldn't send any response to the client.
If you have a Web API that throws an unhandled exception, ELMAH shows:
So how do we catch these unhandled Web API exceptions and route them to ELMAH?
One good option is to use an Exception Filter. If you're familiar with exception filters in MVC, it's the same idea, but instead of implementing System.Web.Mvc.IExceptionFilter, we need to implement System.Web.Http.Filters.IExceptionFilter (note the namespace difference), and creating a derived class from System.Web.Http.Filters.ExceptionFilterAttribute is an easy approach.
In our derived class, we'll simply override the OnException method and make an explicit log call to ELMAH.
Then, we wire-up our custom exception filter globally by adding our filter:
Now when one of our Web API's throws an unhandled exception, ELMAH picks it up.
Hope this helps, Mark
You can also use ELMAH Web API contrib package (nuget.org/.../Elmah.Contrib.WebApi) for this purpose rather than writing an extra handler.
Good to know. Thanks for sharing that Teoman.
Really appreciate your post. It was the most straightforward one on this topic I was able to find. BTW, some may find it useful that in non MVC contexts you can use the following in Application_Start() to register the filter: