capa logging

Logging Inteligente de Requisições em APIs .NET — Captura Segura e Estruturada de Request Body

1. Contexto e Motivação

Em APIs modernas, o log de requisições é uma das ferramentas mais valiosas para observabilidade e diagnóstico.

Mas capturar o request body com segurança e eficiência é desafiador: o HttpContext.Request.Body é um stream forward-only, e a leitura incorreta pode:

  • Quebrar o model binding;
  • Causar vazamento de dados sensíveis (LGPD, PCI DSS, HIPAA…);
  • Prejudicar performance e custo de armazenamento.

Este artigo apresenta uma implementação robusta e segura para registrar requisições — inclusive o corpo — com:

  • Redação automática de dados sensíveis;
  • Buffering eficiente e limite de tamanho;
  • Suporte a logs estruturados e Serilog;
  • Integração com middlewares de exceção e correlação.

2. Arquitetura de Logging

A solução é composta por dois middlewares complementares:

MiddlewareResponsabilidadeFase
RequestLoggingMiddlewareCaptura e registra corpo, cabeçalhos e statusSempre executado
ExceptionMiddlewareIntercepta exceções, inclui corpo da requisição no log de erroExecutado apenas em falhas

Essa separação segue o princípio Single Responsibility, garantindo clareza e extensibilidade.

3. Middleware de Logging de Requisição

RequestLoggingMiddleware lê o corpo da requisição com EnableBuffering() e repassa o stream intacto ao pipeline:

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var body = await RequestBodyReader.SafeReadAsync(context, 64 * 1024, new[] { "application/json" });
        context.Items["__reqBody__"] = body.BodyForLog;

        await _next(context);

        _logger.LogInformation(
            "HTTP {Method} {Status} {Path} | User={User} | Body={Body}",
            context.Request.Method,
            context.Response.StatusCode,
            context.Request.Path,
            context.User?.Identity?.Name ?? "anonymous",
            body.BodyForLog);
    }
}

4. Leitura Segura do Body

O helper RequestBodyReader garante que o conteúdo seja lido de forma segura e redigida:

public static class RequestBodyReader
{
    public static async Task<(string BodyForLog, string RawBody)> SafeReadAsync(
        HttpContext ctx, int maxBytes, string[] allowedContentTypes)
    {
        var req = ctx.Request;
        if (!allowedContentTypes.Any(ct => req.ContentType?.StartsWith(ct, StringComparison.OrdinalIgnoreCase) == true))
            return ("(body skipped: type not allowed)", "");

        req.EnableBuffering();
        using var mem = new MemoryStream();
        await req.Body.CopyToAsync(mem);
        var body = Encoding.UTF8.GetString(mem.ToArray());
        req.Body.Position = 0;

        return (Redact(body), body);
    }

    private static string Redact(string body)
    {
        var sensitive = new[] { "password", "token", "secret", "cpf" };
        foreach (var key in sensitive)
            body = body.Replace(key, "***redacted***", StringComparison.OrdinalIgnoreCase);
        return body;
    }
}

5. Middleware de Exceção Integrado

ExceptionMiddleware aproveita o body já lido pelo middleware anterior, sem custo extra:

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionMiddleware> _logger;

    public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            var body = context.Items["__reqBody__"] as string ?? "(unavailable)";
            _logger.LogError(ex, "Unhandled exception: {Method} {Url} | Body={Body}",
                context.Request.Method,
                context.Request.Path,
                body);

            context.Response.StatusCode = 500;
            await context.Response.WriteAsJsonAsync(new { error = "Server Error" });
        }
    }
}

6. Registro no Pipeline

app.UseMiddleware<RequestLoggingMiddleware>();
app.UseMiddleware<ExceptionMiddleware>();
app.UseRouting();
app.MapControllers();

7. Conformidade e Segurança

A implementação atende recomendações de:

  • ISO 27001 A.12.4.1 – Registro de eventos de auditoria;
  • SOC 2 CC7.2 – Detecção de falhas e evidência de eventos;
  • NIST CSF DE.AE-3 – Logging e análise de anomalias;
  • LGPD Art. 46 – Proteção de dados sensíveis e anonimização.

Medidas adotadas:

  • Redação de campos sensíveis (passwordtoken, etc.);
  • Tamanho máximo de corpo (maxBytes);
  • Exclusão de uploads (multipart/form-data);
  • Amostragem controlada (SampleRate);
  • Logs estruturados (compatíveis com Serilog/Elastic).

8. Extensões Possíveis

  • Response Logging: captura do corpo de resposta para debugging avançado;
  • Correlation ID Middleware: para rastrear requisições ponta a ponta;
  • Contexto de Usuário (Claims): inclusão de UserId ou TenantId;
  • Exportação para Elastic + Kibana: análise visual e alertas automáticos.

9. Conclusão

Com essa arquitetura, é possível atingir observabilidade de nível enterprise sem comprometer segurança ou performance.

O segredo está no equilíbrio: capturar o que é essencial, mascarar o que é sensível, e registrar o suficiente para diagnosticar qualquer incidente.