Test run of HTTP Logging in ASP.NET Core 6

Ever wanted to log all HTTP requests to your websites? I sure have and I have been implemented various solutions for doing so for years. From HTTP modules in ASP.NET to request logging middleware in ASP.NET Core. With ASP.NET Core, we now have request logging built-in. Stay tuned to learn more.

Let's jump directly into some code. I'll use an ASP.NET Core MVC sample created from the default template for this post, but HTTP Logging is applicable for all of the templates. Requests from HTTP Logging are transmitted as log messages through the Microsoft.Extensions.Logging framework. The destination of the log messages, therefore, depends on how you configure Microsoft.Extensions.Logging and which loggers you set up. To enable HTTP Logging, include the following code in the Program.cs file:

app.UseHttpLogging();

Logging is produced by the Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware class which (as you can see from the namespace) is beneath Microsoft.AspNetCore.*. I'm mentioning this since HTTP Logging messages are information messages and these are not enabled as default when creating the project from the default template. You can adjust the log level for Microsoft.AspNetCore to Information but I wouldn't recommend doing that.

Would your users appreciate fewer errors?

➡️ Reduce errors by 90% with elmah.io error logging and uptime monitoring ⬅️

ASP.NET Core produces a lot of info messages which will quickly clutter your log. The better option is to control the HTTP logging middleware class specifically by including the following line to the Logging > LogLevel configuration in the appsettings.json file:

"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"

When running the project, you will see the new log message in the Output windows inside Visual Studio:

When configuring a Microsoft.Extensions.Logging provider other than the default logging to the console, both the request and response will show up in a more structural form. In this example I have configured my application to log to elmah.io through the Elmah.Io.Extensions.Logging package which will store the information messages on elmah.io:

For obvious reasons and to better demo how the log messages look, I'm using elmah.io here. But you can use any log destination like files, Elasticsearch, SQL Server, or whatever technology you prefer. As shown in both screenshots, the HTTP request is simply a concatenated string of keys and values. In the case of Visual Studio, each key/value pair is shown on a new line while in elmah.io, everything is included in the same line. This is because of a small issue in ASP.NET Core 6 which has been fixed. Hopefully, there will be a minor release with this fix before ASP.NET Core 7. While waiting for the fix, we can use options for HTTP Logging to make this look prettier:

builder.Services.AddHttpLogging(options =>
{
    options.LoggingFields = HttpLoggingFields.RequestMethod | HttpLoggingFields.RequestPath;
});

Using the LoggingFields property on HTTP Logging options, you can specify which headers and information to include in the log message (All being the default). I'd wish for greater flexibility on how to generate the message. But with the configuration above, you'd get information messages looking similar to this:

Request: Method: GET PathBase: Path: /

As an alternative or supplement, you can use the RequestHeaders and ResponseHeaders properties on the options. These contain a set of headers that are allowed as part of the log message. You can remove a header:

options.ResponseHeaders.Remove("Content-Type");

Or you can add custom headers:

options.RequestHeaders.Add("X-API-Key");

There are a couple of additional options that I won't go into here.

Overall, HTTP Logging is a nice feature that allows for some level of flexibility. I'm looking forward to getting the previously mentioned fix released to allow for better visualization of HTTP logging in storage engines supporting structured log messages. I'd also like to see some improved ways of specifying how to generate the log message. But for now, I'm just happy that this feature is now baked into the framework.