Registrazione HTTP in ASP.NET Core

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione ASP.NET Core 8.0 di questo articolo.

La registrazione HTTP è un middleware che registra informazioni sulle richieste HTTP in ingresso e sulle risposte HTTP. La registrazione HTTP fornisce log per:

  • Informazioni sulle richieste HTTP
  • Proprietà comuni
  • Intestazioni
  • Corpo
  • Informazioni sulle risposte HTTP

La registrazione HTTP può:

  • Registra tutte le richieste e le risposte o solo richieste e risposte che soddisfano determinati criteri.
  • Selezionare le parti della richiesta e della risposta registrate.
  • Consente di redigire le informazioni riservate dai log.

La registrazione HTTP può ridurre le prestazioni di un'app, soprattutto quando si registrano i corpi di richiesta e risposta. Prendere in considerazione l'impatto sulle prestazioni durante la selezione dei campi da registrare. Testare l'impatto sulle prestazioni delle proprietà di registrazione selezionate.

Avviso

La registrazione HTTP può registrare potenzialmente informazioni personali. Tenere conto di questo rischio ed evitare la registrazione di informazioni sensibili.

Abilitare la registrazione HTTP

La registrazione HTTP è abilitata chiamando AddHttpLogging e UseHttpLogging, come illustrato nell'esempio seguente:

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

L'espressione lambda vuota nell'esempio precedente di chiamata AddHttpLogging aggiunge il middleware con la configurazione predefinita. Per impostazione predefinita, la registrazione HTTP registra proprietà comuni, ad esempio percorso, codice di stato e intestazioni per richieste e risposte.

Aggiungere la riga seguente al file appsettings.Development.json al livello "LogLevel": { in modo da visualizzare i log HTTP:

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

Con la configurazione predefinita, una richiesta e una risposta vengono registrate come coppia di messaggi simili all'esempio seguente:

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

Opzioni di registrazione HTTP

Per configurare le opzioni globali per il middleware di registrazione HTTP, chiamare AddHttpLogging in Program.csusando l'espressione lambda per configurare 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();

Nota

Nell'esempio precedente e negli esempi seguenti viene UseHttpLogging chiamato dopo UseStaticFiles, quindi la registrazione HTTP non è abilitata per i file statici. Per abilitare la registrazione HTTP dei file statici, chiamare UseHttpLogging prima UseStaticFilesdi .

LoggingFields

HttpLoggingOptions.LoggingFields è un flag di enumerazione che configura parti specifiche della richiesta e della risposta da registrare. Il valore predefinito di HttpLoggingOptions.LoggingFields è RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders e ResponseHeaders

RequestHeaders e ResponseHeaders sono set di intestazioni HTTP registrate. I valori di intestazione vengono registrati solo per i nomi di intestazione presenti in queste raccolte. Il codice seguente aggiunge sec-ch-ua a RequestHeaders, in modo che il valore dell'intestazione sec-ch-ua venga registrato. E aggiunge MyResponseHeader a ResponseHeaders, in modo che il valore dell'intestazione MyResponseHeader venga registrato. Se queste righe vengono rimosse, i valori di queste intestazioni sono [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 fornisce la configurazione per la selezione della codifica da usare per un tipo di supporto specifico.

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

Questo approccio può essere usato anche per abilitare la registrazione per i dati non registrati per impostazione predefinita, ad esempio i dati del modulo, che potrebbero avere un tipo di supporto, application/x-www-form-urlencoded ad esempio o multipart/form-data.

Metodi MediaTypeOptions

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

L'impostazione CombineLogs di per true configurare il middleware per consolidare tutti i log abilitati per una richiesta e una risposta in un unico log alla fine. Sono inclusi la richiesta, il corpo della richiesta, la risposta, il corpo della risposta e la durata.

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

Configurazione specifica dell'endpoint

Per la configurazione specifica dell'endpoint in app per le API minime, è disponibile un WithHttpLogging metodo di estensione. L'esempio seguente illustra come configurare la registrazione HTTP per un endpoint:

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

Per la configurazione specifica dell'endpoint nelle app che usano controller, l'attributo [HttpLogging] è disponibile. L'attributo può essere usato anche nelle app per le API minime, come illustrato nell'esempio seguente:

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

IHttpLoggingInterceptor

IHttpLoggingInterceptor è l'interfaccia per un servizio che può essere implementato per gestire i callback per richiesta e risposta per personalizzare i dettagli registrati. Tutte le impostazioni di log specifiche dell'endpoint vengono applicate per prime e possono quindi essere sottoposte a override in questi callback. Un'implementazione può:

  • Esaminare una richiesta o una risposta.
  • Abilitare o disabilitare qualsiasi oggetto HttpLoggingFields.
  • Regolare la quantità di corpo della richiesta o della risposta registrata.
  • Aggiungere campi personalizzati ai log.

Registrare un'implementazione IHttpLoggingInterceptor chiamando AddHttpLoggingInterceptor<T> in Program.cs. Se vengono registrate più IHttpLoggingInterceptor istanze, vengono eseguite nell'ordine registrato.

L'esempio seguente illustra come registrare un'implementazione IHttpLoggingInterceptor :

var builder = WebApplication.CreateBuilder(args);

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

L'esempio seguente è un'implementazione IHttpLoggingInterceptor che:

  • Controlla il metodo di richiesta e disabilita la registrazione per le richieste POST.
  • Per le richieste non POST:
    • Redacts request path, request headers e response headers .Redacts request path, request headers, and response headers.
    • Aggiunge campi e valori di campo personalizzati ai log delle richieste e delle risposte.
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");
    }
}

Con questo intercettore, una richiesta POST non genera alcun log anche se la registrazione HTTP è configurata per registrare HttpLoggingFields.All. Una richiesta GET genera log simili all'esempio seguente:

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

Ordine di configurazione della registrazione della precedenza

L'elenco seguente mostra l'ordine di precedenza per la configurazione della registrazione:

  1. Configurazione globale da HttpLoggingOptions, impostata chiamando AddHttpLogging.
  2. La configurazione specifica dell'endpoint dall'attributo o il metodo di estensione esegue l'override [HttpLogging]WithHttpLogging della configurazione globale.
  3. IHttpLoggingInterceptor viene chiamato con i risultati e può modificare ulteriormente la configurazione per richiesta.

La registrazione HTTP è un middleware che registra informazioni sulle richieste HTTP in ingresso e sulle risposte HTTP. La registrazione HTTP fornisce log per:

  • Informazioni sulle richieste HTTP
  • Proprietà comuni
  • Intestazioni
  • Corpo
  • Informazioni sulle risposte HTTP

La registrazione HTTP risulta utile in diversi scenari per:

  • Registrare informazioni sulle richieste e le risposte in ingresso.
  • Filtrare le parti della richiesta e della risposta registrate.
  • Filtrare le intestazioni da registrare.

La registrazione HTTP può ridurre le prestazioni di un'app, soprattutto quando si registrano i corpi delle richieste e delle risposte. Prendere in considerazione l'impatto sulle prestazioni durante la selezione dei campi da registrare. Testare l'impatto sulle prestazioni delle proprietà di registrazione selezionate.

Avviso

La registrazione HTTP può registrare potenzialmente informazioni personali. Tenere conto di questo rischio ed evitare la registrazione di informazioni sensibili.

Abilitazione della registrazione HTTP

La registrazione HTTP viene abilitata con UseHttpLogging, che aggiunge il middleware di registrazione 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();

Per impostazione predefinita, la registrazione HTTP registra proprietà comuni, come percorso, codice di stato e intestazioni per le richieste e le risposte. Aggiungere la riga seguente al file appsettings.Development.json al livello "LogLevel": { in modo da visualizzare i log HTTP:

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

L'output viene registrato come singolo messaggio in LogLevel.Information.

Sample request output

Opzioni di registrazione HTTP

Per configurare il middleware di registrazione HTTP, chiamare AddHttpLogging in 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();

Nota

Nell'esempio precedente e negli esempi seguenti viene UseHttpLogging chiamato dopo UseStaticFiles, quindi la registrazione HTTP non è abilitata per il file statico. Per abilitare la registrazione HTTP dei file statici, chiamare UseHttpLogging prima UseStaticFilesdi .

LoggingFields

HttpLoggingOptions.LoggingFields è un flag di enumerazione che configura parti specifiche della richiesta e della risposta da registrare. Il valore predefinito di HttpLoggingOptions.LoggingFields è RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders

Headers sono un set di intestazioni di richiesta HTTP che possono essere registrate. I valori di intestazione vengono registrati solo per i nomi di intestazione presenti in questa raccolta. Il codice seguente registra l'intestazione della richiesta "sec-ch-ua". Se si rimuove logging.RequestHeaders.Add("sec-ch-ua");, il valore dell'intestazione della richiesta "sec-ch-ua" viene corretto. Il codice evidenziato seguente chiama HttpLoggingOptions.RequestHeaders e 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 fornisce la configurazione per la selezione della codifica da usare per un tipo di supporto specifico.

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

Questo approccio può essere usato anche per abilitare la registrazione per i dati non registrati per impostazione predefinita (ad esempio, dati del modulo, che potrebbero avere un tipo di supporto, application/x-www-form-urlencoded ad esempio o multipart/form-data).

Metodi MediaTypeOptions

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