Registro HTTP en ASP.NET Core

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión .NET 8 de este artículo.

Registro HTTP es un middleware que registra información sobre las solicitudes HTTP entrantes y las respuestas HTTP. El registro HTTP proporciona registros de:

  • Información de solicitud HTTP
  • Propiedades comunes
  • Encabezados
  • Cuerpo
  • Información de respuesta HTTP

Registro HTTP:

  • Registre todas las solicitudes y respuestas o solo solicitudes y respuestas que cumplan determinados criterios.
  • Filtrar las partes de la solicitud y la respuesta que se registran.
  • Permite censurar información confidencial de los registros.

Registro HTTP puede reducir el rendimiento de una aplicación, especialmente al registrar los cuerpos de solicitud y respuesta. Tenga en cuenta el impacto en el rendimiento al seleccionar los campos que se van a registrar. Pruebe cómo inciden en el rendimiento las propiedades de registro seleccionadas.

Advertencia

Registro HTTP puede registrar potencialmente información de identificación personal (PII). Tenga en cuenta el riesgo y evite registrar información confidencial.

Habilitación del registro HTTP

El registro HTTP está habilitado mediante una llamada a AddHttpLogging y UseHttpLogging, como se muestra en el ejemplo siguiente:

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

La expresión lambda vacía del ejemplo anterior de llamada agrega AddHttpLogging el middleware con la configuración predeterminada. De forma predeterminada, Registro HTTP registra propiedades comunes como ruta de acceso, código de estado y encabezados para solicitudes y respuestas.

Agregue la siguiente línea al archivo appsettings.Development.json en el nivel "LogLevel": { para que se muestren los registros HTTP:

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

Con la configuración predeterminada, se registra una solicitud y una respuesta como un par de mensajes similares al ejemplo siguiente:

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

Opciones de Registro HTTP

Para configurar las opciones globales para el middleware de registro HTTP, llame a AddHttpLogging en Program.cs, mediante la expresión lambda para configurar 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:

En el ejemplo anterior y los ejemplos siguientes, UseHttpLogging se llama después de UseStaticFiles, por lo que el registro HTTP no está habilitado para el archivo estático. Para habilitar el registro HTTP de archivos estáticos, llame a UseHttpLogging antes de a UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields es una marca de enumeración que configura partes específicas de la solicitud y respuesta para registrar. HttpLoggingOptions.LoggingFields tiene RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders como valor predeterminado.

RequestHeaders y ResponseHeaders

RequestHeaders y ResponseHeaders son conjuntos de encabezados HTTP que se registran. Los valores de encabezado solo se registran para los nombres de encabezado que están en esta colección. El código siguiente agrega sec-ch-ua a RequestHeaders, por lo que se registra el valor del sec-ch-ua encabezado. Y agrega MyResponseHeader a ResponseHeaders, por lo que el valor del MyResponseHeader encabezado se registra. Si se quitan estas líneas, los valores de estos encabezados son [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 proporciona la configuración para seleccionar la codificación que se va a usar para un tipo de medio específico.

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

Este enfoque también se puede usar para habilitar el registro de datos que no se registran de forma predeterminada (por ejemplo, datos de formulario, que pueden tener un tipo de medio como application/x-www-form-urlencoded o multipart/form-data).

Métodos de MediaTypeOptions

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

Al establecer CombineLogs en true se configura el middleware para consolidar todos sus registros habilitados para una solicitud y respuesta en un registro al final. Esto incluye la solicitud, el cuerpo de la solicitud, la respuesta, el cuerpo de la respuesta y la duración.

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

Configuración específica del punto de conexión

Para la configuración específica del punto de conexión en aplicaciones de API mínimas, hay disponible un WithHttpLogging método de extensión. En el ejemplo siguiente se muestra cómo configurar el registro HTTP para un punto de conexión:

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

Para la configuración específica del punto de conexión en aplicaciones que usan controladores, el [HttpLogging] atributo está disponible. El atributo también se puede usar en aplicaciones de API mínimas, como se muestra en el ejemplo siguiente:

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

IHttpLoggingInterceptor

IHttpLoggingInterceptor es la interfaz de un servicio que se puede implementar para controlar las devoluciones de llamada por solicitud y por respuesta para personalizar los detalles que se registran. Cualquier configuración de registro específica del punto de conexión se aplica primero y, a continuación, se puede invalidar en estas devoluciones de llamada. Una implementación puede:

  • Inspeccione una solicitud o respuesta.
  • Habilite o deshabilite cualquier HttpLoggingFields.
  • Ajuste la cantidad del cuerpo de la solicitud o respuesta que se registra.
  • Agregue campos personalizados a los registros.

Registre una IHttpLoggingInterceptor implementación llamando a AddHttpLoggingInterceptor<T> en Program.cs. Si se registran varias IHttpLoggingInterceptor instancias, se ejecutan en el orden registrado.

En el ejemplo siguiente se muestra cómo registrar una IHttpLoggingInterceptor implementación:

var builder = WebApplication.CreateBuilder(args);

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

En el siguiente ejemplo se muestra una implementación de IHttpLoggingInterceptor:

  • Inspecciona el método de solicitud y deshabilita el registro de las solicitudes POST.
  • Para solicitudes que no son POST:
    • Redacta la ruta de acceso de solicitud, los encabezados de solicitud y los encabezados de respuesta.
    • Agrega campos personalizados y valores de campo a los registros de solicitud y respuesta.
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 este interceptor, una solicitud POST no genera ningún registro aunque el registro HTTP esté configurado para registrar HttpLoggingFields.All. Una solicitud GET genera registros similares al ejemplo siguiente:

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

Orden de configuración de registro de prioridad

En la lista siguiente se muestra el orden de prioridad para la configuración de registro:

  1. Configuración global de HttpLoggingOptions, establecida llamando a AddHttpLogging.
  2. La configuración específica del punto de conexión del [HttpLogging] atributo o el método de WithHttpLogging extensión invalida la configuración global.
  3. IHttpLoggingInterceptor se llama a con los resultados y puede modificar aún más la configuración por solicitud.

Registro HTTP es un middleware que registra información sobre las solicitudes HTTP entrantes y las respuestas HTTP. El registro HTTP proporciona registros de:

  • Información de solicitud HTTP
  • Propiedades comunes
  • Encabezados
  • Cuerpo
  • Información de respuesta HTTP

Registro HTTP es útil en varios escenarios para:

  • Registrar información sobre las solicitudes y respuestas entrantes.
  • Filtrar las partes de la solicitud y la respuesta que se registran.
  • Filtrar los encabezados que se registrarán.

El registro HTTP puede reducir el rendimiento de una aplicación, especialmente al registrar los cuerpos de solicitud y respuesta. Tenga en cuenta el impacto en el rendimiento al seleccionar los campos que se van a registrar. Pruebe cómo inciden en el rendimiento las propiedades de registro seleccionadas.

Advertencia

Registro HTTP puede registrar potencialmente información de identificación personal (PII). Tenga en cuenta el riesgo y evite registrar información confidencial.

Habilitación de Registro HTTP

Registro HTTP está habilitado con UseHttpLogging, que agrega middleware de registro 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();

De forma predeterminada, Registro HTTP registra propiedades comunes como ruta de acceso, código de estado y encabezados para solicitudes y respuestas. Agregue la siguiente línea al archivo appsettings.Development.json en el nivel "LogLevel": { para que se muestren los registros HTTP:

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

La salida se registra como un único mensaje en LogLevel.Information.

Salida de solicitud de ejemplo

Opciones de Registro HTTP

Para configurar el middleware de registro HTTP, llame a AddHttpLogging en 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:

En el ejemplo anterior y los ejemplos siguientes, UseHttpLogging se llama después de UseStaticFiles, por lo que el registro HTTP no está habilitado para el archivo estático. Para habilitar el registro HTTP de archivos estáticos, llame a UseHttpLogging antes de a UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields es una marca de enumeración que configura partes específicas de la solicitud y respuesta para registrar. HttpLoggingOptions.LoggingFields tiene RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders como valor predeterminado.

RequestHeaders

Headers son un conjunto de encabezados de solicitud HTTP que se pueden registrar. Los valores de encabezado solo se registran para los nombres de encabezado que están en esta colección. El código siguiente registra el encabezado de solicitud "sec-ch-ua". Si se quita logging.RequestHeaders.Add("sec-ch-ua");, se redacta el valor del encabezado de solicitud "sec-ch-ua". El código resaltado siguiente llama a HttpLoggingOptions.RequestHeaders y 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 proporciona la configuración para seleccionar la codificación que se va a usar para un tipo de medio específico.

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

Este enfoque también se puede usar para habilitar el registro de datos que no se registran de forma predeterminada (por ejemplo, datos de formulario, que pueden tener un tipo de medio como application/x-www-form-urlencoded o multipart/form-data).

Métodos de MediaTypeOptions

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