Freigeben über


HTTP-Protokollierung in ASP.NET Core

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Die HTTP-Protokollierung ist eine Middleware, die Informationen zu eingehenden HTTP-Anforderungen und HTTP-Antworten protokolliert. Die HTTP-Protokollierung enthält Protokolle zu:

  • HTTP-Anforderungsinformationen
  • Allgemeine Eigenschaften
  • Header
  • Körper
  • HTTP-Antwortinformationen

Die HTTP-Protokollierung kann:

  • alle Anforderungen und Antworten oder nur Anforderungen und Antworten protokollieren, die bestimmten Kriterien entsprechen
  • auswählen, welche Teile der Anforderung und Antwort protokolliert werden
  • Ermöglichen Sie das Entfernen vertraulicher Informationen aus den Protokollen.

Die HTTP-Protokollierung kann die Leistung einer App verringern, insbesondere beim Protokollieren der Anforderungs- und Antworttexte. Berücksichtigen Sie die Auswirkungen auf die Leistung, wenn Sie die zu protokollierenden Felder auswählen. Testen Sie die Auswirkungen der ausgewählten Protokollierungseigenschaften auf die Leistung.

Warnung

Die HTTP-Protokollierung kann potenziell personenbezogene Informationen (Personally Identifiable Information, PII) protokollieren. Berücksichtigen Sie das Risiko, und vermeiden Sie die Protokollierung vertraulicher Informationen. Weitere Informationen zur Schwärzung finden Sie unter Schwärzen vertraulicher Daten.

Aktivieren der HTTP-Protokollierung

Die HTTP-Protokollierung wird durch Aufrufen von AddHttpLogging und UseHttpLogging aktiviert, wie im folgenden Beispiel gezeigt:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Hello World!");

app.Run();

Der leere Lambda im vorherigen Beispiel des Aufrufs AddHttpLogging fügt die Middleware mit der Standardkonfiguration hinzu. Standardmäßig protokolliert die HTTP-Protokollierung allgemeine Eigenschaften wie Pfad, Statuscode und Header für Anforderungen und Antworten.

Fügen Sie der Datei appsettings.Development.json auf der Ebene "LogLevel": { die folgende Zeile hinzu, damit die HTTP-Protokolle angezeigt werden:

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

Bei der Standardkonfiguration werden eine Anforderung und eine Antwort als Nachrichtenpaar protokolliert, das dem folgenden Beispiel ähnelt:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Protocol: HTTP/2
      Method: GET
      Scheme: https
      PathBase:
      Path: /
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
      Host: localhost:52941
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.61
      Accept-Encoding: gzip, deflate, br
      Accept-Language: en-US,en;q=0.9
      Upgrade-Insecure-Requests: [Redacted]
      sec-ch-ua: [Redacted]
      sec-ch-ua-mobile: [Redacted]
      sec-ch-ua-platform: [Redacted]
      sec-fetch-site: [Redacted]
      sec-fetch-mode: [Redacted]
      sec-fetch-user: [Redacted]
      sec-fetch-dest: [Redacted]
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      StatusCode: 200
      Content-Type: text/plain; charset=utf-8
      Date: Tue, 24 Oct 2023 02:03:53 GMT
      Server: Kestrel

HTTP-Protokollierungsoptionen

Um globale Optionen für die HTTP-Protokollierungsmiddleware zu konfigurieren, rufen Sie AddHttpLogging in Program.cs auf, indem Sie den Lambdaausdruck verwenden, um HttpLoggingOptions zu konfigurieren.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging();

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Hinweis

Im vorherigen Beispiel und in den folgenden Beispielen wird UseHttpLogging nach UseStaticFiles ausgeführt, sodass die HTTP-Protokollierung für statische Dateien nicht aktiviert ist. Um die HTTP-Protokollierung für statische Dateien zu aktivieren, rufen Sie UseHttpLogging vor UseStaticFiles auf.

LoggingFields

HttpLoggingOptions.LoggingFields ist ein Enumerationsflag, das bestimmte Teile der zu protokollierenden Anforderung und Antwort konfiguriert. LoggingFields ist standardmäßig RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders und ResponseHeaders

RequestHeaders und ResponseHeaders sind die HTTP-Header, die protokolliert werden. Headerwerte werden nur für Headernamen protokolliert, die in diesen Sammlungen enthalten sind. Der folgende Code fügt sec-ch-ua zu RequestHeaders hinzu, sodass der Wert des sec-ch-ua-Headers protokolliert wird. Außerdem wird MyResponseHeader zu ResponseHeaders hinzugefügt, sodass der Wert des MyResponseHeader-Headers protokolliert wird. Wenn diese Zeilen entfernt werden, lauten die Werte dieser Header [Redacted].

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging();

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

MediaTypeOptions

MediaTypeOptions stellt die Konfiguration für die Auswahl der für einen bestimmten Medientyp zu verwendenden Codierung bereit.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging();

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Dieser Ansatz kann auch verwendet werden, um die Protokollierung für Daten zu ermöglichen, die standardmäßig nicht protokolliert werden (z. B. Formulardaten, deren Medientyp application/x-www-form-urlencoded oder multipart/form-data lauten kann).

MediaTypeOptions Methoden

RequestBodyLogLimit und ResponseBodyLogLimit

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging();

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

CombineLogs

Wenn Sie CombineLogs auf true festlegen, wird die Middleware so konfiguriert, dass alle aktivierten Protokolle für eine Anforderung und Antwort in einem Protokoll am Ende konsolidiert werden. Das umfasst die Anforderung, den Anforderungstext, die Antwort, den Antworttext und die Dauer.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging();

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Endpunktspezifische Konfiguration

Für endpunktspezifische Konfigurationen in minimalen API-Apps ist eine WithHttpLogging-Erweiterungsmethode verfügbar. Das folgende Beispiel zeigt, wie die HTTP-Protokollierung für einen Endpunkt konfiguriert wird:

app.MapGet("/response", () => "Hello World! (logging response)")
    .WithHttpLogging(HttpLoggingFields.ResponsePropertiesAndHeaders);

Für endpunktspezifische Konfigurationen in Apps, die Controller verwenden, ist das [HttpLogging] Attribut verfügbar. Das Attribut kann auch in minimalen API-Apps verwendet werden, wie im folgenden Beispiel gezeigt:

app.MapGet("/duration", [HttpLogging(loggingFields: HttpLoggingFields.Duration)]
    () => "Hello World! (logging duration)");

IHttpLoggingInterceptor

IHttpLoggingInterceptor ist die Schnittstelle für einen Dienst, der implementiert werden kann, um Rückrufe pro Anforderung und Antwort zu verarbeiten und anzupassen, welche Details protokolliert werden. Alle endpunktspezifischen Protokolleinstellungen werden zuerst angewendet und können dann in diesen Rückrufen überschrieben werden. Eine Implementierung kann:

  • Untersuchen Sie eine Anforderung oder Antwort.
  • beliebige HttpLoggingFields aktivieren oder deaktivieren
  • anpassen, welcher Anteil des Anforderungs- oder Antworttexts protokolliert wird
  • benutzerdefinierte Felder zu Protokollen hinzufügen

Registrieren Sie eine IHttpLoggingInterceptor-Implementierung, indem Sie AddHttpLoggingInterceptor in Program.cs aufrufen. Wenn mehrere IHttpLoggingInterceptor-Instanzen registriert sind, werden sie in der registrierten Reihenfolge ausgeführt.

Das folgende Beispiel zeigt, wie Sie eine IHttpLoggingInterceptor-Implementierung registrieren:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.Duration;
});
builder.Services.AddHttpLoggingInterceptor<SampleHttpLoggingInterceptor>();

Das folgende Beispiel ist eine IHttpLoggingInterceptor-Implementierung, die:

  • die Anforderungsmethode untersucht und die Protokollierung für POST-Anforderungen deaktiviert
  • Für Nicht-POST-Anforderungen:
    • Redigiert den Anforderungspfad, die Anforderungsheader und die Antwortheader.
    • den Anforderungs- und Antwortprotokollen benutzerdefinierte Felder und Feldwerte hinzufügt
using Microsoft.AspNetCore.HttpLogging;

namespace HttpLoggingSample;

internal sealed class SampleHttpLoggingInterceptor : IHttpLoggingInterceptor
{
    public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
    {
        if (logContext.HttpContext.Request.Method == "POST")
        {
            // Don't log anything if the request is a POST.
            logContext.LoggingFields = HttpLoggingFields.None;
        }

        // Don't enrich if we're not going to log any part of the request.
        if (!logContext.IsAnyEnabled(HttpLoggingFields.Request))
        {
            return default;
        }

        if (logContext.TryDisable(HttpLoggingFields.RequestPath))
        {
            RedactPath(logContext);
        }

        if (logContext.TryDisable(HttpLoggingFields.RequestHeaders))
        {
            RedactRequestHeaders(logContext);
        }

        EnrichRequest(logContext);

        return default;
    }

    public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext)
    {
        // Don't enrich if we're not going to log any part of the response
        if (!logContext.IsAnyEnabled(HttpLoggingFields.Response))
        {
            return default;
        }

        if (logContext.TryDisable(HttpLoggingFields.ResponseHeaders))
        {
            RedactResponseHeaders(logContext);
        }

        EnrichResponse(logContext);

        return default;
    }

    private void RedactPath(HttpLoggingInterceptorContext logContext)
    {
        logContext.AddParameter(nameof(logContext.HttpContext.Request.Path), "RedactedPath");
    }

    private void RedactRequestHeaders(HttpLoggingInterceptorContext logContext)
    {
        foreach (var header in logContext.HttpContext.Request.Headers)
        {
            logContext.AddParameter(header.Key, "RedactedHeader");
        }
    }

    private void EnrichRequest(HttpLoggingInterceptorContext logContext)
    {
        logContext.AddParameter("RequestEnrichment", "Stuff");
    }

    private void RedactResponseHeaders(HttpLoggingInterceptorContext logContext)
    {
        foreach (var header in logContext.HttpContext.Response.Headers)
        {
            logContext.AddParameter(header.Key, "RedactedHeader");
        }
    }

    private void EnrichResponse(HttpLoggingInterceptorContext logContext)
    {
        logContext.AddParameter("ResponseEnrichment", "Stuff");
    }
}

Mit diesem Interceptor generiert eine POST-Anforderung keine Protokolle, auch wenn die HTTP-Protokollierung so konfiguriert ist, dass HttpLoggingFields.All protokolliert wird. Eine GET-Anforderung generiert Protokolle, die dem folgenden Beispiel ähneln:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Path: RedactedPath
      Accept: RedactedHeader
      Host: RedactedHeader
      User-Agent: RedactedHeader
      Accept-Encoding: RedactedHeader
      Accept-Language: RedactedHeader
      Upgrade-Insecure-Requests: RedactedHeader
      sec-ch-ua: RedactedHeader
      sec-ch-ua-mobile: RedactedHeader
      sec-ch-ua-platform: RedactedHeader
      sec-fetch-site: RedactedHeader
      sec-fetch-mode: RedactedHeader
      sec-fetch-user: RedactedHeader
      sec-fetch-dest: RedactedHeader
      RequestEnrichment: Stuff
      Protocol: HTTP/2
      Method: GET
      Scheme: https
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      Content-Type: RedactedHeader
      MyResponseHeader: RedactedHeader
      ResponseEnrichment: Stuff
      StatusCode: 200
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[4]
      ResponseBody: Hello World!
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[8]
      Duration: 2.2778ms

Rangfolge der Protokollierungskonfiguration

Die folgende Liste zeigt die Rangfolge für die Protokollierungskonfiguration:

  1. Die globale Konfiguration von HttpLoggingOptions wird durch Aufrufen von AddHttpLogging festgelegt.
  2. Endpunktspezifische Konfiguration aus dem [HttpLogging] Attribut oder der WithHttpLogging Erweiterungsmethode setzt globale Konfiguration außer Kraft.
  3. IHttpLoggingInterceptor wird mit den Ergebnissen aufgerufen und kann die Konfiguration pro Anforderung weiter ändern.

HTTP-Protokollierung ist eine Middleware, die Informationen zu eingehenden HTTP-Anforderungen und HTTP-Antworten protokolliert. Die HTTP-Protokollierung enthält Protokolle zu:

  • HTTP-Anforderungsinformationen
  • Allgemeine Eigenschaften
  • Header
  • Körper
  • HTTP-Antwortinformationen

Die HTTP-Protokollierung ist hilfreich in verschiedenen Szenarios wie:

  • Aufzeichnen von Informationen zu eingehenden Anforderungen und Antworten.
  • Filtern, welche Teile der Anforderung und Antwort protokolliert werden.
  • Filtern, welche Header protokolliert werden sollen.

Die HTTP-Protokollierung kann die Leistung einer App verringern, insbesondere beim Protokollieren der Anforderungs- und Antworttexte. Berücksichtigen Sie die Auswirkungen auf die Leistung, wenn Sie die zu protokollierenden Felder auswählen. Testen Sie die Auswirkungen der ausgewählten Protokollierungseigenschaften auf die Leistung.

Warnung

Die HTTP-Protokollierung kann potenziell personenbezogene Informationen (Personally Identifiable Information, PII) protokollieren. Berücksichtigen Sie das Risiko, und vermeiden Sie die Protokollierung vertraulicher Informationen.

Aktivieren der HTTP-Protokollierung

Die HTTP-Protokollierung wird mit UseHttpLogging aktiviert, wodurch Middleware für die HTTP-Protokollierung hinzufügt wird.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Hello World!");

app.Run();

Standardmäßig protokolliert die HTTP-Protokollierung allgemeine Eigenschaften wie Pfad, Statuscode und Header für Anforderungen und Antworten. Fügen Sie der Datei appsettings.Development.json auf der Ebene "LogLevel": { die folgende Zeile hinzu, damit die HTTP-Protokolle angezeigt werden:

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

Die Ausgabe wird als einzelne Nachricht unter LogLevel.Information protokolliert.

Beispielanforderungsausgabe

HTTP-Protokollierungsoptionen

Um die Middleware für die HTTP-Protokollierung zu konfigurieren, rufen Sie AddHttpLogging in Program.cs auf.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Hinweis

Im vorherigen Beispiel und den folgenden Beispielen wird UseHttpLogging nach UseStaticFiles aufgerufen. Die HTTP-Protokollierung ist also für statische Dateien nicht aktiviert. Um die HTTP-Protokollierung für statische Dateien zu aktivieren, rufen Sie UseHttpLogging vor UseStaticFiles auf.

LoggingFields

HttpLoggingOptions.LoggingFields ist ein Enumerationsflag, das bestimmte Teile der zu protokollierenden Anforderung und Antwort konfiguriert. HttpLoggingOptions.LoggingFields ist standardmäßig RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders

Headers ist eine Gruppe von HTTP-Anforderungsheadern, die protokolliert werden dürfen. Headerwerte werden nur für Headernamen protokolliert, die in dieser Sammlung enthalten sind. Der folgende Code protokolliert den Anforderungsheader "sec-ch-ua". Beim Entfernen von logging.RequestHeaders.Add("sec-ch-ua"); wird der Wert des Anforderungsheaders "sec-ch-ua" entfernt. Der folgende hervorgehobene Code ruft HttpLoggingOptions.RequestHeaders und HttpLoggingOptions.ResponseHeaders auf:

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

MediaTypeOptions

MediaTypeOptions stellt die Konfiguration für die Auswahl der für einen bestimmten Medientyp zu verwendenden Codierung bereit.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Dieser Ansatz kann auch verwendet werden, um die Protokollierung für Daten zu ermöglichen, die standardmäßig nicht protokolliert werden. Zum Beispiel Formulardaten, die einen Medientyp wie application/x-www-form-urlencoded oder multipart/form-data aufweisen können.

MediaTypeOptions Methoden

RequestBodyLogLimit und ResponseBodyLogLimit

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Maskieren vertraulicher Daten

Http-Protokollierung mit Redaction kann durch Aufrufen AddHttpLoggingRedactionaktiviert werden:

Weitere Informationen zur Daten-Schwärzungs-Bibliothek von .NET finden Sie unter "Daten-Schwärzung in .NET".

Maskierungsoptionen für die Protokollierung

Um Optionen für die Protokollierung mit Redaction zu konfigurieren, rufen Sie AddHttpLoggingRedaction in Program.cs mithilfe der Lambda-Funktion auf, um LoggingRedactionOptions zu konfigurieren:

using Microsoft.Extensions.Compliance.Classification;

namespace HttpLoggingSample
{
    public static class MyTaxonomyClassifications
    {
        public static string Name => "MyTaxonomy";

        public static DataClassification Private => new(Name, nameof(Private));
        public static DataClassification Public => new(Name, nameof(Public));
        public static DataClassification Personal => new(Name, nameof(Personal));
    }
}
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();

Mit der vorherigen Redaktionskonfiguration ähnelt die Ausgabe dem Folgenden:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[9]
      Request and Response:
      server.address: localhost:61361
      Path: /
      http.request.header.accept:
      Protocol: HTTP/2
      Method: GET
      Scheme: https
      http.response.header.content-type:
      StatusCode: 200
      Duration: 8.4684
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/2 GET https://localhost:61361/ - 200 - text/plain;+charset=utf-8 105.5334ms

Hinweis

Der Anforderungspfad /home wird nicht protokolliert, da er in der ExcludePathStartsWith Eigenschaft enthalten ist. http.request.header.accept und http.response.header.content-type wurden von Microsoft.Extensions.Compliance.Redaction.ErasingRedactor redigiert.

RequestPathLoggingMode

RequestPathLoggingMode bestimmt, wie der Anforderungspfad protokolliert wird, ob Formatted oder Structured, festgelegt von IncomingPathLoggingMode:

  • Formatted: Protokolliert den Anforderungspfad ohne Parameter.
  • Structured: Protokolliert den Anforderungspfad mit eingeschlossenen Parametern.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();

RequestPathParameterRedactionMode

RequestPathParameterRedactionModeGibt an, wie Routenparameter im Anforderungspfad redigiert werden sollen, ob Strict, Loose oder None, festgelegt durch HttpRouteParameterRedactionMode:

  • Strict: Anforderungsroutenparameter werden als vertraulich betrachtet, erfordern explizite Anmerkungen mit einer Datenklassifizierung und sind standardmäßig redigiert.
  • Loose: Alle Parameter werden standardmäßig als nicht vertraulich betrachtet und enthalten as-is.
  • None: Routenparameter werden unabhängig vom Vorhandensein von Datenklassifizierungsanmerkungen nicht redigiert.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();

RequestHeadersDataClasses

RequestHeadersDataClasses ordnet Anforderungsheader ihrer Datenklassifizierung zu, die bestimmt, wie sie redigiert werden:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();

ResponseHeadersDataClasses

ResponseHeadersDataClasses, ähnlich wie RequestHeadersDataClasses', aber für Antwortheader:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();

RouteParameterDataClasses

RouteParameterDataClasses ordnet Routenparameter ihrer Datenklassifizierung zu:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();

ExcludePathStartsWith

ExcludePathStartsWith Gibt Pfade an, die vollständig von der Protokollierung ausgeschlossen werden sollen:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();

IncludeUnmatchedRoutes

IncludeUnmatchedRoutes ermöglicht das Melden nicht übereinstimmender Routen. Wenn auf true festgelegt, wird der gesamte Pfad der Routen protokolliert, die nicht durch Routing identifiziert werden, anstatt den Wert von Unknown für das Pfadattribut zu protokollieren.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();

builder.Services.AddHttpLoggingRedaction(op =>
{
    op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
    op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
    op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
    op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
    op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
    {
        { "one", MyTaxonomyClassifications.Personal },
    };
    // Add the paths that should be filtered, with a leading '/'.
    op.ExcludePathStartsWith.Add("/home");
    op.IncludeUnmatchedRoutes = true;
});

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");

app.Run();