Compartilhar via


Registro em log HTTP no ASP.NET Core

Note

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.

Warning

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão do .NET 9 deste artigo.

O registro HTTP é um middleware que registra informações sobre solicitações e respostas HTTP recebidas. O registro em HTTP fornece logs de:

  • Informações de solicitação HTTP
  • Propriedades comuns
  • Headers
  • Body
  • Informações de resposta HTTP

O registro em log HTTP pode:

  • Registre todas as solicitações e respostas ou apenas as solicitações e respostas que atendam a determinados critérios.
  • Selecione quais partes da solicitação e da resposta são registradas em log.
  • Permite que você exclua informações confidenciais dos logs.

O log HTTP pode reduzir o desempenho de um aplicativo, especialmente ao registrar em log os corpos de solicitação e resposta. Considere o impacto sobre o desempenho ao selecionar campos para registrar. Teste o impacto sobre o desempenho das propriedades de log selecionadas.

Warning

O registro em log HTTP pode registrar PII (informações de identificação pessoal). Considere o risco e evite registrar informações confidenciais. Para obter mais informações sobre redação, verifique a redação de dados confidenciais

Habilitar o registro em log HTTP

O registro em log HTTP é habilitado chamando AddHttpLogging e UseHttpLogging, conforme mostrado no exemplo a seguir:

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

O lambda vazio no exemplo anterior de chamada AddHttpLogging adiciona o middleware com a configuração padrão. Por padrão, o registro em log HTTP registra propriedades comuns, como caminho, código de status e cabeçalhos para solicitações e respostas.

Adicione a seguinte linha ao arquivo appsettings.Development.json no nível "LogLevel": { para que os logs HTTP sejam exibidos:

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

Com a configuração padrão, uma solicitação e resposta é registrada como um par de mensagens semelhante ao exemplo a seguir:

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

Opções de registro em log HTTP

Para configurar opções globais para o middleware de registro de log HTTP, chame AddHttpLogging em Program.cs, usando o 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();

Note

No exemplo anterior e nos exemplos a seguir, UseHttpLogging é chamado depois de UseStaticFiles, então o registro em log HTTP não está habilitado para arquivos estáticos. Para habilitar o log HTTP do arquivo estático, chame UseHttpLogging antes de UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields é um sinalizador de enumeração que configura partes específicas da solicitação e da resposta para serem registradas. LoggingFields usa como padrão RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders e ResponseHeaders

RequestHeaders e ResponseHeaders são conjuntos de cabeçalhos HTTP registrados. Os valores de cabeçalho são registrados apenas para nomes de cabeçalho que estão nestas coleções. O código a seguir adiciona sec-ch-ua ao RequestHeaders, para que o valor do cabeçalho sec-ch-ua seja registrado. O código a seguir adiciona MyResponseHeader ao ResponseHeaders, para que o valor do cabeçalho MyResponseHeader seja registrado. Se essas linhas forem removidas, os valores desses cabeçalhos serão [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 fornece configuração para selecionar qual codificação usar para um tipo de mídia 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();

Essa abordagem também pode ser usada para habilitar o registro em log de dados que não são registrados por padrão (por exemplo, dados de formulário, que podem ter um tipo de mídia como application/x-www-form-urlencoded ou multipart/form-data).

Métodos 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

Definir CombineLogs como true configura o middleware para consolidar todos os seus logs habilitados para uma solicitação e resposta em um único log no final. Isso inclui a solicitação, o corpo da solicitação, a resposta, o corpo da resposta e a duração.

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

Configuração específica do ponto de extremidade

Para a configuração específica de endpoints em aplicativos de APIs mínimas, está disponível um método de extensão WithHttpLogging. O exemplo a seguir mostra como configurar o log de HTTP para um endpoint.

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

O atributo [HttpLogging] está disponível para a configuração específica do ponto de extremidade em aplicativos que usam controladores. O atributo também pode ser usado em aplicativos de API mínimos, conforme mostrado no exemplo a seguir:

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

IHttpLoggingInterceptor

IHttpLoggingInterceptor é a interface de um serviço que pode ser implementado para lidar com retornos de chamada por solicitação e por resposta para personalizar quais detalhes são registrados. As configurações de log específicas do ponto de extremidade são aplicadas primeiro e podem ser substituídas nesses retornos de chamada. Uma implementação pode:

  • Inspecione uma solicitação ou resposta.
  • Habilite ou desabilite qualquer HttpLoggingFields.
  • Ajuste o quanto do corpo da solicitação ou resposta é registrado nos logs.
  • Adicione campos personalizados aos logs.

Registre uma implementação IHttpLoggingInterceptor chamando AddHttpLoggingInterceptor em Program.cs. Se várias instâncias IHttpLoggingInterceptor forem registradas, elas serão executadas na ordem registrada.

O exemplo a seguir mostra como registrar uma implementação IHttpLoggingInterceptor:

var builder = WebApplication.CreateBuilder(args);

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

O exemplo a seguir é uma implementação IHttpLoggingInterceptor que:

  • Inspeciona o método de solicitação e desabilita o registro em log para solicitações POST.
  • Para solicitações não POST:
    • Reduz o caminho da solicitação, os cabeçalhos da solicitação e os cabeçalhos da resposta.
    • Adiciona campos personalizados e valores de campo aos logs de solicitação e resposta.
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");
    }
}

Com esse interceptador, uma solicitação POST não gera nenhum log, mesmo que o registro em log HTTP esteja configurado para registrar HttpLoggingFields.All. Uma solicitação GET gera registros semelhantes ao exemplo a seguir:

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

Ordem de precedência da configuração de registro em log

A lista a seguir mostra a ordem de precedência da configuração de registro em log:

  1. Configuração global de HttpLoggingOptions, definida pela chamada de AddHttpLogging.
  2. A configuração específica do ponto de extremidade do [HttpLogging] atributo ou do WithHttpLogging método de extensão substitui a configuração global.
  3. IHttpLoggingInterceptor é chamado com os resultados e pode modificar ainda mais a configuração por solicitação.

O Registro HTTP é um middleware que registra informações sobre as solicitações HTTP recebidas e as respostas HTTP. O registro em HTTP fornece logs de:

  • Informações de solicitação HTTP
  • Propriedades comuns
  • Headers
  • Body
  • Informações de resposta HTTP

O registro em log HTTP é útil em vários cenários para:

  • Registrar informações sobre solicitações e respostas de entrada.
  • Filtrar quais partes da solicitação e da resposta são registradas.
  • Filtrar quais cabeçalhos registrar.

O registro em log HTTP pode reduzir o desempenho de um aplicativo, especialmente ao registrar em log os corpos de solicitação e resposta. Considere o impacto sobre o desempenho ao selecionar campos para registrar. Teste o impacto sobre o desempenho das propriedades de log selecionadas.

Warning

O registro em log HTTP pode registrar PII (informações de identificação pessoal). Considere o risco e evite registrar informações confidenciais.

Habilitando o registro em log HTTP

O registro em log HTTP é habilitado com UseHttpLogging, que adiciona o middleware de registro em log 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();

Por padrão, o registro em log HTTP registra propriedades comuns, como caminho, código de status e cabeçalhos, para solicitações e respostas. Adicione a seguinte linha ao arquivo appsettings.Development.json no nível "LogLevel": { para que os logs HTTP sejam exibidos:

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

A saída é registrada como uma mensagem em LogLevel.Information.

Saída de solicitação modelo

Opções de registro em log HTTP

Para configurar o middleware de registro em log HTTP, chame AddHttpLogging em 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

No exemplo anterior e nos exemplos a seguir, UseHttpLogging é chamado após UseStaticFiles, portanto, o registro em log HTTP não está habilitado para arquivo estático. Para habilitar o log HTTP do arquivo estático, chame UseHttpLogging antes de UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields é um sinalizador de enumeração que configura partes específicas da solicitação e da resposta para serem registradas. HttpLoggingOptions.LoggingFields usa como padrão RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders

Headers são um conjunto de cabeçalhos de solicitação HTTP que têm permissão para ser registrados. Os valores de cabeçalho são registrados apenas para nomes de cabeçalho que estão nesta coleção. O código a seguir registra o cabeçalho de solicitação "sec-ch-ua". Se logging.RequestHeaders.Add("sec-ch-ua"); for removido, o valor do cabeçalho da solicitação "sec-ch-ua" será redigido. O código destacado a seguir chama 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 fornece configuração para selecionar qual codificação usar para um tipo de mídia 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();

Essa abordagem também pode ser usada para habilitar o registro em log de dados que não são registrados por padrão. Por exemplo, dados de formulário, que podem ter um tipo de mídia como application/x-www-form-urlencoded ou multipart/form-data.

Métodos 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();

Redigir dados confidenciais

O registro HTTP com ocultação pode ser habilitado chamando AddHttpLoggingRedaction:

var builder = WebApplication.CreateBuilder(args);

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

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

Para obter mais informações sobre a biblioteca de redação de dados do .NET, consulte Redação de dados no .NET.

Opções de redação de log

Para configurar opções de registro com redação em AddHttpLoggingRedaction, chame Program.cs usando o lambda para configurar 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();

Com a configuração de redação anterior, a saída é semelhante à seguinte:

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

O caminho /home da solicitação não é registrado porque está incluído na ExcludePathStartsWith propriedade. http.request.header.accept e http.response.header.content-type foram redigidos por Microsoft.Extensions.Compliance.Redaction.ErasingRedactor.

RequestPathLoggingMode

RequestPathLoggingMode determina como o caminho da solicitação é registrado, se Formatted ou Structured, definido por IncomingPathLoggingMode:

  • Formatted: registra o caminho da solicitação sem parâmetros.
  • Structured: registra o caminho da solicitação com parâmetros incluídos.
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 especifica como os parâmetros de rota no caminho da solicitação devem ser redigidos, seja Strict, Looseou None, definidos por HttpRouteParameterRedactionMode:

  • Strict: os parâmetros de rota de solicitação são considerados confidenciais, exigem anotação explícita com uma classificação de dados e são redigidos por padrão.
  • Loose: todos os parâmetros são considerados não confidenciais e incluídos as-is por padrão.
  • None: os parâmetros de rota não são redigidos independentemente da presença de anotações de classificação de dados.
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 mapeia cabeçalhos de solicitação para sua classificação de dados, que determina como eles são redigidos:

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, semelhante a RequestHeadersDataClasses', mas para cabeçalhos de resposta:

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 mapeia parâmetros de rota para a classificação dos dados:

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 especifica caminhos que devem ser excluídos do registro em log inteiramente:

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 permite informar rotas não correspondentes. Se definido como true, registra o caminho inteiro das rotas não identificadas pelo Roteamento em vez de registrar o valor de Unknown para o atributo path:

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