ASP.NET Web API exception logging with Raygun.io

Β· 802 words Β· 4 minutes to read

Jon Galloway recently wrote a monster 4-part series covering the new features of MVC 5.1 and Web API 2.1 releases. One thing he mentioned was the new IExceptionLogger for Web API, and he called out the community to provide some example implementations of it.

Therefore, let’s have a look at how you’d approach that - with a sample of logging Exceptions to the excellent Raygun service from Mindscape.

What is IExceptionLogger? πŸ”—

Exception loggers provide you a single point into which you can plug in a service that would log information about any exceptions occurring in the Web API pipeline - regardless where they originated from. Moreover, the relevant ExceptionContext will be provided to you inside that logger, giving you access to contextual information which are specific for the controller or filter or any other Web API component from which the exception was thrown.

The great thing about exception loggers is that they will always be called - even if the exception occurs in funky edge situation such as when writing to the response stream from the media type formatter.
Finally, an important point is that you can register multiple ExceptionLoggers in Web API, so you can pipe this exception information into different targets.

IExceptionLogger exposes a single async method that is rather self explanatory.

public interface IExceptionLogger  
{  
Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken);  
}  

Instead of implementing the logger interface by hand, you can take advantage of the abstract base class - ExceptionLogger, which wraps the single async interface method into both sync and async virtual methods and allows you to override either of those. It also provides a virtual ShouldLog methods which is used to determine whether the exception should be logged.

public abstract class ExceptionLogger : IExceptionLogger  
{  
public virtual Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken);  
public virtual void Log(ExceptionLoggerContext context);  
public virtual bool ShouldLog(ExceptionLoggerContext context);  
}  

Using Raygun with Web API πŸ”—

Raygun is a great service, allowing you to send all exceptions over HTTP to them and they will manage them and provide all kinds of interesting views, filters and management options for your exception details. This beats any text based logger by a country mile!

Raygun already has a .NET client but it’s System.Web & HttpContext.Current based and as such does not work with Web API, except the web host, and even that is quite limited. It wouldn’t respect the semantics of HttpRequestMessage and Web API specific objects such as RequestContext.

Actually, there have already been community efforts to build a Raygun client for Nancy, which also doesn’t use HttpContext - so it’s high time we have something similar for Web API too. So, I have created a project to do just on Github that and published it on Nuget under the WebApiContrib umbrella.

Install-Package WebApiContrib.Logging.Raygun  

To plug it into your Web API simply register the Raygun logger into your API config:

config.Services.Add(typeof (IExceptionLogger), new RaygunExceptionLogger("YOUR\_API\_KEY"));  

or using the app.config/web.config:

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

<appSettings>  
<add key="RaygunAppKey" value="YOUR\_API\_KEY" />  
</appSettings>  

How does it work? πŸ”—

The implementation is extremely simple - in the LogAsync of our custom logger, we construct the message to be sent to Raygun and dispatch it over HTTP.

public class RaygunExceptionLogger : ExceptionLogger  
{  
private readonly string _apiKey;

public RaygunExceptionLogger() : this(ConfigurationManager.AppSettings["RaygunAppKey"])  
{}

public RaygunExceptionLogger(string apiKey)  
{  
if (apiKey == null) throw new ArgumentNullException("apiKey");  
_apiKey = apiKey;  
}

public async override Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)  
{  
if (!string.IsNullOrWhiteSpace(_apiKey))  
{  
var message = new WebApiRaygunMessage  
{  
Details =  
{  
Request = new WebApiRaygunRequestMessage(context.Request),  
Error = new WebApiRaygunErrorMessage(context.Exception),  
Environment = new WebApiRaygunEnvironmentMessage(),  
User = new WebApiRaygunIdentifierMessage(context.RequestContext != null && context.RequestContext.Principal != null ? context.RequestContext.Principal.Identity.Name : "Anonymous"),  
Client = new WebApiRaygunClientMessage()  
}  
};

var client = new HttpClient();  
var msg = new HttpRequestMessage(HttpMethod.Post, "https://api.raygun.io/entries");  
msg.Headers.Add("X-ApiKey", _apiKey);  
msg.Content = new ObjectContent<WebApiRaygunMessage>(message, new JsonMediaTypeFormatter());

await client.SendAsync(msg, cancellationToken);  
}  
}  
}  

The individual semantics behind the message sent to Raygun are not important here - they simply gather all kinds of info such as stack trace, system information, userinformation and request data. All of them are neatly presented in your Raygun dashboard afterwards.

For now I decided to now use a background threa for it because ThreadPool.QueueUserWorkItem is evil and using it is not a good idea in such libraries. If there is a need to add, let’s consider using WebBackgrounder instead -obviously any contributions are welcome!

Also, if you have any other ideas - let me know. We could easily include things such as data from the HttpConfiguration object or the HttpRequestMessage properties. Or even include information about which catch block was involved too.

Trying it out πŸ”—

Once you set up an account with Raygun you can pick up the key, install the RaygunExceptionLogger and start sending out the exceptions.

Below are a couple of screenshots from exceptions I threw in different places in Web API.

Overview

raygun_exceptions

Summary

summary

Environment

environment

Headers

headers

About


Hi! I'm Filip W., a cloud architect from ZΓΌrich πŸ‡¨πŸ‡­. I like Toronto Maple Leafs πŸ‡¨πŸ‡¦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁒󠁳󠁣󠁴󠁿.

You can find me on Github, on Mastodon and on Bluesky.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP