25.09.2017 - READING TIME: 5 minutes
Web API Global Exception Logger/Handler | Part 2
Author: Irhad Babić
The previous blog post includes 'How to intercept all incoming requests and How to store data per request with WebAPI'. That post explains a process that was actually a build-up for a bigger solution I was working on. Basically, I had a need for intercepting and logging all of the unhandled exceptions. Here is how I've done it.
First I signed up for Loggly, and then set it up in just a few short moments. Setup could not be easier.
Open the Package Manager Console, select the project you want the package installed for, and install the Loggly package by running
Configure log4net by adding an attribute to your AssemblyInfo.cs
Add a log4net config section to your project’s web.config/app.config file so it can talk to Loggly. You can just simply copy (re-write) my code below:
And now test your Log4net/Loggly configuration by simply running
And then checking your Loggly dashboard if the “your log message” appeared there.
If the answer is yes, let’s move on.
I needed to be able to identify request from which the exception originated. In case multiple exceptions occur during a single API call, I needed to be able to identify this easily. For this, I chose to generate a guid prior to each API request execution, and store it for the lifetime of the request. I would send this guid with every log item over to Loggly. I covered and elaborated the whole process in my last post, but here’s a basic gist of it. Create a public class which inherits from ActionFilterAttribute. Then you override OnActionExecuting(HttpActionContext actionContext) method, and add your code to it.
Then you override OnActionExecuting(HttpActionContext actionContext) method, and add your code to it.
Make sure you create the guid and store it prior to calling the base method. Store the guid into HttpContext.Current.Items collection as it’s scope is a single request and it’s lifetime is the same.
What’s left in order for interception to start working is registering the above method with GlobalFilters in Global.asax.cs, or with Filters in your WebApiConfig.
If you’re running your project on .NET Core you won’t be able to register your filter in Global.asax.cs or WebApiConfig. What you can do is you can register a filter globally (for all controllers and actions) by adding it to the MvcOptions.Filters collection in the ConfigureServices method in the Startup class
Run the project, put a breakpoint on the first line of the override method we created and make an API call to any of the API methods you have written. Make sure this override method runs BEFORE execution enters the API method you just invoked.
This is the last step. We have everything we need, Log4net is set up for Loggly, we have all our API calls intercepted, and we have a unique ID available during the lifetime of each request. Let’s actually implement logging for every unhandled exception.
First, you create another filter attribute, but this time it should inherit from ExceptionFilterAttribute. Override the OnException method, and add your code.
I opted for using a simple list of dictionary items, but there are more options available, which I will not discuss here as this post is getting too long, although it’s not complicated really. Check out the Loggly documentation if you have different needs than mine. Basically, I added everything of relevance in this list (the guid, Exception, Call Stack, etc).
You can use your newly created filter attribute selectively on the method by method basis, but in my case I needed it hooked up with every API method. Clearly, in order to achieve this, the filter attribute needs to be registered, just as the previous (interception) one, so add the line for this one too.
Now add a line of code, that’s sure to cause an exception to be thrown, to one of your API methods, but don’t handle it yourself. For example:
Run the project, put a breakpoint at the beginning of your logging code and invoke the API method containing the above code line. Make sure the unhandled exception that will occur will be intercepted in your custom filter attribute code. After the execution is completed, allow 20-40 seconds for Loggly to process the data you just sent, and then visit and refresh your Loggly dashboard in order to see if all the information has been properly logged. If you followed this tutorial to the letter, it should look something like this.
I needed only the unhandled exceptions to be logged, but you can use this same approach and introduce default handling for all those exceptions. It’s just the matter of customizing your ExceptionHandlingAttribute code to your needs in order to do so.
Hope this post helped. Let me know if there’s something unclear, or if you need help implementing this.