DEV Community

Thomas Ardal
Thomas Ardal

Posted on • Originally published at blog.elmah.io

Error Logging Middleware in ASP.NET Core

This post is about the concept of middleware in ASP.NET Core. The post is named Error Logging Middleware in ASP.NET Core because I want to use error logging as an example of utilizing middleware. The concepts around middleware shown in the examples throughout this post aren't bound to error logging in any way and can be used as a foundation for building all types of middleware.

Middleware are code components executed as part of the request pipeline in Core. If you have a background in ASP.NET and think this sounds familiar, you're right. Middleware is pretty much HTTP modules as you know it from ASP.NET. The biggest difference between modules and middleware is really how you configure it. Modules are configured in web.config and since Core doesn't use the concept of a web.config, you configure middleware in C#. If you know Express for Node.js, you will find that configuring middleware heavily borrow a lot of concepts from that.

Let's wait with more babbling about middleware and look at an example. Middleware in its most simple form is a C# class. Let's create some error logging middleware:

public class ErrorLoggingMiddleware
{
    private readonly RequestDelegate _next;

    public ErrorLoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine($"The following error happened: {e.Message}");
            throw;
        }
    }
}

In order for our middleware to work, we need to implement two things. A constructor accepting a RequestDelegate and an Invoke method. The contructor is called a single time, but the underlying delegate will change from request to request. All middleware components are responsible for either executing the next link in the pipeline (_next) or terminate the pipeline by not calling _next. In our case, we want to execute the rest of the pipeline in order to catch any exceptions happening while processing the HTTP request. When an exception is caught, we log a message to System.Diagnostics.Debug. In real life, you probably want to log somewhere better, but for the demo, we want the exception to show up inside Visual Studio only.

Notice that the catch-block throw the exception after logging it to System.Diagnostics. Throwing the exception makes sure that other pieces of middleware handling exceptions still work.

To tell Core about our new and shiny piece of middleware, configure it in Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory fac)
{
    ...
    app.UseMiddleware<ErrorLoggingMiddleware>();
    ...
}

Make sure to call the UseMiddleware-method after installing other pieces of middleware handling exceptions (like UseExceptionHandler). Some middleware classes "swallow" the exception, which results in your catch block not being triggered. By adding your middleware as the last piece, the class is invoked closer to the failing code.

To test the middleware, make the Index-method in HomeController throw an exception:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        throw new Exception("Some error yo");
    }
}

When starting the project, the exception is now logged to the Output window in Visual Studio:

Debug message in output

Success! We just implemented our first piece of functional middleware for Core.

While calling the UseMiddleware<> method in Startup.cs definitely work, it is considered the good practice to create a custom Use-method:

public static class ErrorLoggingMiddlewareExtensions
{
    public static IApplicationBuilder UseErrorLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<ErrorLoggingMiddleware>();
    }
}

Call the static method in Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory fac)
{
    ...
    app.UseErrorLogging();
    ...
}

For an example on how to implement fully featured error logging middleware for Core, check out our elmah.io support for ASP.NET Core on GitHub.

Would your users appreciate fewer errors?

elmah.io is the easy error logging and uptime monitoring service for .NET. Take back control of your errors with support for all .NET web and logging frameworks.

➡️ Error Monitoring for .NET Web Applications ⬅️

This article first appeared on the elmah.io blog at https://blog.elmah.io/error-logging-middleware-in-aspnetcore/

Top comments (0)