Udostępnij za pomocą


Rejestrowanie HTTP w usłudze ASP.NET Core

Note

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z aktualną wersją, zobacz artykuł w wersji .NET 10.

Warning

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, przeczytaj artykuł w wersji .NET 9.

Rejestrowanie HTTP to oprogramowanie pośredniczące, które rejestruje informacje o przychodzących żądaniach HTTP i odpowiedziach HTTP. Rejestrowanie HTTP zapewnia dostęp do dzienników:

  • Informacje o żądaniu HTTP
  • Wspólne właściwości
  • Headers
  • Body
  • Informacje o odpowiedzi HTTP

Rejestrowanie HTTP może:

  • Rejestruj wszystkie żądania i odpowiedzi lub tylko żądania i odpowiedzi spełniające określone kryteria.
  • Wybierz, które części żądania i odpowiedzi są rejestrowane.
  • Umożliwia redagowania poufnych informacji z dzienników.

Rejestrowanie HTTP może zmniejszyć wydajność aplikacji, szczególnie podczas rejestrowania treści żądania i odpowiedzi. Podczas wybierania pól do rejestrowania należy wziąć pod uwagę wpływ na wydajność. Przetestuj wpływ wybranych ustawień logowania na wydajność.

Warning

Rejestrowanie HTTP może potencjalnie rejestrować dane osobowe . Rozważ ryzyko i unikaj rejestrowania poufnych informacji. Aby uzyskać więcej informacji na temat redagowania, sprawdź redagowanie poufnych danych

Włączanie rejestrowania HTTP

Rejestrowanie HTTP jest włączone przez wywołanie AddHttpLogging i UseHttpLogging, jak pokazano w poniższym przykładzie:

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();

Pusta lambda w poprzednim przykładzie wywołania AddHttpLogging dodaje oprogramowanie pośredniczące z konfiguracją domyślną. Domyślnie rejestrowanie HTTP rejestruje typowe właściwości, takie jak ścieżka, kod stanu i nagłówki dla żądań i odpowiedzi.

Dodaj następujący wiersz do appsettings.Development.json pliku na "LogLevel": { poziomie, aby wyświetlić dzienniki HTTP:

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

W przypadku konfiguracji domyślnej żądanie i odpowiedź są rejestrowane jako para komunikatów podobnych do następującego przykładu:

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

Opcje rejestrowania HTTP

Aby skonfigurować globalne opcje oprogramowania pośredniczącego rejestrowania HTTP, wywołaj AddHttpLogging w Program.cs, używając wyrażenia lambda do skonfigurowania HttpLoggingOptions.

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();

Note

W poprzednim przykładzie i poniższych przykładach UseHttpLogging jest wywoływana po UseStaticFiles, więc rejestrowanie HTTP nie jest włączone dla plików statycznych. Aby włączyć rejestrowanie HTTP pliku statycznego, wywołaj metodę UseHttpLogging przed UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields to flaga wyliczenia, która konfiguruje określone części żądania i odpowiedzi na dziennik. LoggingFields wartość domyślna to RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders i ResponseHeaders

RequestHeaders i ResponseHeaders to zestawy zarejestrowanych nagłówków HTTP. Wartości nagłówka są rejestrowane tylko dla nazw nagłówków, które znajdują się w tych kolekcjach. Poniższy kod dodaje sec-ch-ua do RequestHeaders, więc wartość nagłówka sec-ch-ua jest zapisywana. Dodaje MyResponseHeader do ResponseHeaders, co powoduje, że wartość nagłówka MyResponseHeader jest rejestrowana. Jeśli te wiersze zostaną usunięte, wartości tych nagłówków to [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 Udostępnia konfigurację do wybierania, które kodowanie ma być używane dla określonego typu nośnika.

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();

Takie podejście może również służyć do włączania rejestrowania danych, które nie są rejestrowane domyślnie (na przykład dane formularza, które mogą mieć typ nośnika, taki jak application/x-www-form-urlencoded lub multipart/form-data).

Metody MediaTypeOptions

RequestBodyLogLimit i 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

Ustawienie CombineLogs na true konfiguruje middleware, aby skonsolidować wszystkie aktywne dzienniki dla żądania i odpowiedzi w jeden dziennik na końcu. Obejmuje to żądanie, treść żądania, odpowiedź, treść odpowiedzi i czas trwania.

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();

Konfiguracja specyficzna dla punktu końcowego

W przypadku konfiguracji specyficznej dla punktu końcowego w minimalnych aplikacjach API dostępna jest metoda rozszerzenia. W poniższym przykładzie pokazano, jak skonfigurować rejestrowanie HTTP dla jednego punktu końcowego:

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

W przypadku konfiguracji specyficznej dla punktu końcowego w aplikacjach korzystających z kontrolerów [HttpLogging] atrybut jest dostępny. Atrybut może być również używany w minimalnych aplikacjach interfejsu API, jak pokazano w poniższym przykładzie:

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

IHttpLoggingInterceptor

IHttpLoggingInterceptor to interfejs usługi, który można zaimplementować w celu obsługi wywołań zwrotnych dla poszczególnych żądań i odpowiedzi w celu dostosowania szczegółów, które są rejestrowane. Wszystkie ustawienia dziennika specyficzne dla punktu końcowego są stosowane najpierw, a następnie można je przesłonić w tych wywołaniach zwrotnych. Implementacja może:

  • Sprawdź żądanie lub odpowiedź.
  • Włącz lub wyłącz dowolny HttpLoggingFields.
  • Dostosuj liczbę rejestrowanych treści żądania lub odpowiedzi.
  • Dodaj pola niestandardowe do dzienników.

Zarejestruj implementację IHttpLoggingInterceptor , wywołując polecenie AddHttpLoggingInterceptor w pliku Program.cs. Jeśli zarejestrowano wiele IHttpLoggingInterceptor wystąpień, są one uruchamiane w kolejności zarejestrowanej.

W poniższym przykładzie pokazano, jak zarejestrować implementację IHttpLoggingInterceptor :

var builder = WebApplication.CreateBuilder(args);

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

Poniższy przykład to implementacja IHttpLoggingInterceptor , która:

  • Sprawdza metodę żądania i wyłącza rejestrowanie żądań POST.
  • W przypadku żądań innych niż POST:
    • Redaguje ścieżkę żądania, nagłówki żądania i nagłówki odpowiedzi.
    • Dodaje niestandardowe pola i wartości pól do dzienników żądań i odpowiedzi.
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");
    }
}

W przypadku tego przechwytywania żądanie POST nie generuje żadnych dzienników, nawet jeśli rejestrowanie HTTP jest skonfigurowane do rejestrowania HttpLoggingFields.All. Żądanie GET generuje dzienniki podobne do następującego przykładu:

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

Kolejność pierwszeństwa konfiguracji rejestrowania

Poniższa lista przedstawia kolejność pierwszeństwa konfiguracji rejestrowania:

  1. Konfiguracja globalna z HttpLoggingOptions, ustawiona przez wywołanie AddHttpLogging.
  2. Konfiguracja specyficzna dla punktu końcowego z atrybutu[HttpLogging] lub WithHttpLogging metody rozszerzenia zastępuje konfigurację globalną.
  3. IHttpLoggingInterceptor jest wywoływany z wynikami i może dodatkowo modyfikować konfigurację dla każdego żądania.

Rejestrowanie HTTP to oprogramowanie pośredniczące, które rejestruje informacje o przychodzących żądaniach HTTP i odpowiedziach HTTP. Rejestrowanie HTTP zapewnia dostęp do dzienników:

  • Informacje o żądaniu HTTP
  • Wspólne właściwości
  • Headers
  • Body
  • Informacje o odpowiedzi HTTP

Rejestrowanie HTTP jest przydatne w kilku scenariuszach:

  • Rejestruj informacje o przychodzących żądaniach i odpowiedziach.
  • Filtruj, które części żądania i odpowiedzi są rejestrowane.
  • Filtrowanie nagłówków do zapisu w logach

Rejestrowanie HTTP może zmniejszyć wydajność aplikacji, szczególnie podczas rejestrowania treści żądania i odpowiedzi. Podczas wybierania pól do rejestrowania należy wziąć pod uwagę wpływ na wydajność. Przetestuj wpływ wybranych ustawień logowania na wydajność.

Warning

Rejestrowanie HTTP może potencjalnie rejestrować dane osobowe . Rozważ ryzyko i unikaj rejestrowania poufnych informacji.

Włączanie rejestrowania HTTP

Rejestrowanie HTTP jest włączone w programie UseHttpLogging, co dodaje oprogramowanie pośredniczące rejestrowania HTTP.

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();

Domyślnie rejestrowanie HTTP rejestruje typowe właściwości, takie jak ścieżka, kod stanu i nagłówki dla żądań i odpowiedzi. Dodaj następujący wiersz do appsettings.Development.json pliku na "LogLevel": { poziomie, aby wyświetlić dzienniki HTTP:

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

Dane wyjściowe są rejestrowane jako pojedynczy komunikat pod adresem LogLevel.Information.

Przykładowe dane wyjściowe żądania

Opcje rejestrowania HTTP

Aby skonfigurować middleware rejestrowania HTTP, wywołaj AddHttpLogging w Program.cs.

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();

Note

W poprzednim przykładzie i poniższych przykładach UseHttpLogging jest wywoływana po UseStaticFiles, więc rejestrowanie HTTP nie jest włączone dla pliku statycznego. Aby włączyć rejestrowanie HTTP pliku statycznego, wywołaj metodę UseHttpLogging przed UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields to flaga wyliczenia, która konfiguruje określone części żądania i odpowiedzi na dziennik. HttpLoggingOptions.LoggingFields wartość domyślna to RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders

Headers to zestaw nagłówków żądań HTTP, które mogą być rejestrowane. Wartości nagłówka są rejestrowane tylko dla nazw nagłówków, które znajdują się w tej kolekcji. Poniższy kod rejestruje nagłówek żądania "sec-ch-ua". Jeśli logging.RequestHeaders.Add("sec-ch-ua"); zostanie usunięta, wartość nagłówka "sec-ch-ua" żądania zostanie zredagowana. Następujący wyróżniony kod wywołuje HttpLoggingOptions.RequestHeaders i HttpLoggingOptions.ResponseHeaders:

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 Udostępnia konfigurację do wybierania, które kodowanie ma być używane dla określonego typu nośnika.

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();

Takie podejście może również służyć do włączania rejestrowania danych, które nie są rejestrowane domyślnie. Na przykład, dane formularza mogą mieć taki typ nośnika, jak application/x-www-form-urlencoded lub multipart/form-data.

Metody MediaTypeOptions

RequestBodyLogLimit i 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();

Redagowanie poufnych danych

Rejestrowanie HTTP za pomocą funkcji redaction można włączyć, wywołując polecenie AddHttpLoggingRedaction:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddRedaction();
builder.Services.AddHttpLoggingRedaction(op => { });

Aby uzyskać więcej informacji na temat biblioteki zaciemniania danych platformy .NET, zobacz artykuł Zaciemnianie danych w .NET.

Opcje redakcji logów

Aby skonfigurować opcje rejestrowania z redakcją, wywołaj AddHttpLoggingRedaction w Program.cs za pomocą wyrażenia lambda do skonfigurowania LoggingRedactionOptions.

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();

W przypadku poprzedniej konfiguracji edycji, wynik jest podobny do następującego:

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

Note

Ścieżka /home żądania nie jest rejestrowana, ponieważ jest uwzględniona w właściwości ExcludePathStartsWith. http.request.header.accept i http.response.header.content-type zostały zredagowane przez Microsoft.Extensions.Compliance.Redaction.ErasingRedactor.

RequestPathLoggingMode

RequestPathLoggingMode określa, w jaki sposób ścieżka żądania jest rejestrowana, czy Formatted lub Structured, ustawiona przez IncomingPathLoggingMode:

  • Formatted: Rejestruje ścieżkę żądania bez parametrów.
  • Structured: rejestruje ścieżkę żądania z dołączonymi parametrami.
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

RequestPathParameterRedactionMode określa, jak parametry trasy w ścieżce żądania powinny być ukryte, czy Strict, Loose, lub None, ustawione przez HttpRouteParameterRedactionMode:

  • Strict: Parametry trasy żądania są traktowane jako poufne, wymagają jawnej adnotacji z klasyfikacją danych i są domyślnie redagowane.
  • Loose: Wszystkie parametry są traktowane jako niewrażliwe i domyślnie uwzględniane as-is.
  • None: Parametry trasy nie są redagowane niezależnie od obecności adnotacji klasyfikacji danych.
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 mapuje nagłówki żądań na klasyfikację danych, która określa sposób ich redagowania:

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, podobnie jak RequestHeadersDataClassesw przypadku nagłówków odpowiedzi:

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 mapuje parametry trasy do klasyfikacji danych:

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 określa ścieżki, które należy całkowicie wykluczyć z rejestrowania:

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 umożliwia raportowanie niezgodnych tras. Jeśli ustawiono wartość true, loguje całą ścieżkę tras, które nie zostały zidentyfikowane przez Routing zamiast logować wartość Unknown dla atrybutu ścieżki:

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();