Logging HTTP Requests in .NET Core API

Scenario Link to heading

Troubleshooting Web APIs, especially when released to the world, often require the submitted payload, to be able to reproduce the issue. HTTP payloads are not captured by logging be default, and it should be that way to avoid introducing performance and privacy issues, however, when needed, there are many ways to do it.

Some teams will explicitly add code to log the request, for each of the requests where the payload is needed. This is good, but adds a lot of code to maintain, at least one line per API method.

Other teams will create a generic logging mechanism, introduced in runtime via a wrapper on each call or any of the middleware interceptors available in .NET. This is good since it can be maintained in a single place, remains custom, additional code.

My team, looking for ways to add as little code as possible, rely on the build it HttpLogging service. Not customizable as with custom code, but its build need, no need for custom logging code for it to work.

Adding HTTPLogging Link to heading

To add HTTPLogging to a .NET 7 Core APIs project:

  1. In program.cs inside the static main method, when declaring the services but before creating the app
        builder.Services.AddHttpLogging(logging =>
        {
            logging.LoggingFields = HttpLoggingFields.RequestBody;
            logging.RequestBodyLogLimit = 1024;
        });

The LoggingFields properties is a flags property, other fields can be added, here I’m only interested in the request body, other characteristics of the request like the URL and Method are being captured already by the request type logs. The Limit property, which is in bytes, is to prevent polluting the log with very long payloads, either accidental, malicious, or real with endpoints where users submit images.

  1. In program.cs inside the static main method, after creating the app
app.UseWhen(
    context => !(
        context.Request.Path.StartsWithSegments("/api/user/login") ||
        context.Request.Path.StartsWithSegments("/api/user/password")),
    builder => builder.UseHttpLogging());

Here I use the UseWhen filter, to prevent logging passwords. In general, logging secrets or any kind of confidential information should be prevented. More complex APIs might require much more sophisticated mechanisms to avoid privacy incidents while keeping the ability to troubleshoot problematic HTTP requests.

  1. In appsettings.json, in the Logging section, add the relevant lines for Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware based on
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information",
            "Microsoft.AspNetCore": "Warning",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        },
        "ApplicationInsights": {
            "LogLevel": {
                "Default": "Warning",
                "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
            }
        }
    },

The ApplicationInsights section is only needed if that’s where you want your logs to go to.

Finding the logs in Application Insights Link to heading

Running in Visual Studio or looking at the logs stream in Azure App Services will show the entries. In Application Insights the logs will be of type Trace, severity information, and the message starts with RequestBody, for example

Local TimeTypeDetails
9:41:40.179 AMTraceSeverity level: Information, Message: RequestBody: {“productId”:“INFANTRYALPHA2023”,“quantity”:3}