Skip to main content

ILogger no .NET | CSharp

  • ILogger no .NET | CSharp

Visão geral

O ILogger é a abstração padrão de logging do ecossistema moderno do .NET. Ele permite gerar logs estruturados, aplicar filtros por categoria e nível, trocar destinos de saída sem mudar o código de negócio e integrar logging com DI, tracing e observabilidade. No ASP.NET Core, o pipeline de logging é baseado em ILogger, ILoggerFactory e providers, com suporte nativo a logging estruturado e de alto desempenho. (Microsoft Learn)

Arquitetura simplificada:

Código da aplicação

ILogger / ILogger<T>

ILoggerFactory

ILoggerProvider

Console / Debug / EventSource / EventLog / OpenTelemetry / terceiros

Pacotes e namespace

Na maior parte dos casos, você usa:

using Microsoft.Extensions.Logging;

Para os tipos no-op, como NullLogger, NullLoggerFactory e NullLoggerProvider, também é comum usar:

using Microsoft.Extensions.Logging.Abstractions;

O ecossistema de logging do .NET fica em torno de Microsoft.Extensions.Logging e Microsoft.Extensions.Logging.Abstractions. (Microsoft Learn)


Conceitos principais

ILogger

É a interface usada para escrever logs.

Exemplo:

public class PedidoService
{
private readonly ILogger _logger;

public PedidoService(ILogger logger)
{
_logger = logger;
}
}

Na prática, ILogger puro aparece menos em código de aplicação.

ILogger<T>

É a forma mais comum de uso, porque já define a categoria com base no tipo.

public class PedidoService
{
private readonly ILogger<PedidoService> _logger;

public PedidoService(ILogger<PedidoService> logger)
{
_logger = logger;
}
}

ILoggerFactory

É a fábrica usada para criar loggers dinamicamente.

public class PedidoProcessor
{
private readonly ILogger _logger;

public PedidoProcessor(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger("Pedidos.Processamento");
}
}

A orientação oficial para bibliotecas recomenda ILogger<T> quando o logger é interno de uma classe e ILoggerFactory quando a API precisa criar loggers para múltiplas classes ou categorias. (Microsoft Learn)


Categoria do logger

Todo logger possui uma categoria.

Com ILogger<T>, a categoria costuma ser o nome completo do tipo:

MinhaApp.Services.PedidoService

Com ILoggerFactory, a categoria pode ser definida manualmente:

var logger = loggerFactory.CreateLogger("Pagamentos");

A categoria é importante para:

  • filtrar logs
  • separar ruído de framework
  • identificar a origem do log
  • aplicar níveis diferentes por área do sistema

Níveis de log

Os níveis padrão são:

NívelUso
Tracedetalhe extremo, diagnóstico fino
Debugdepuração e investigação
Informationfluxo normal da aplicação
Warninganomalia ou situação inesperada sem quebra imediata
Errorfalha em uma operação
Criticalfalha grave, indisponibilidade ou risco sistêmico
Nonedesabilita logging

Exemplo:

_logger.LogTrace("Iniciando cálculo");
_logger.LogDebug("Valor calculado: {Valor}", valor);
_logger.LogInformation("Pedido criado");
_logger.LogWarning("Pedido demorou mais que o esperado");
_logger.LogError("Erro ao processar pedido");
_logger.LogCritical("Banco de dados indisponível");

Métodos de extensão mais usados

A interface é normalmente consumida por métodos de extensão como:

  • LogTrace
  • LogDebug
  • LogInformation
  • LogWarning
  • LogError
  • LogCritical
  • Log

Exemplo genérico:

_logger.Log(
LogLevel.Information,
new EventId(1001, "PedidoCriado"),
"Pedido {PedidoId} criado com sucesso",
pedidoId);

Na prática, os métodos específicos por nível são mais legíveis.


Structured logging

O modelo recomendado é structured logging, isto é, registrar mensagem com propriedades nomeadas.

Errado:

_logger.LogInformation("Pedido " + pedidoId + " criado");

Correto:

_logger.LogInformation("Pedido {PedidoId} criado", pedidoId);

Vantagens:

  • melhor indexação
  • melhor consulta em observabilidade
  • menos parsing de string
  • melhor padronização

O ASP.NET Core e o .NET destacam explicitamente ILogger como infraestrutura de logs estruturados e de alto desempenho. (Microsoft Learn)


Message template

O texto com placeholders é o message template.

_logger.LogInformation(
"Pedido {PedidoId} processado em {ElapsedMilliseconds}ms para o cliente {ClienteId}",
pedidoId,
elapsedMs,
clienteId);

Boas práticas para nomes:

  • use nomes estáveis
  • prefira nomes claros
  • mantenha consistência entre serviços
  • evite nomes genéricos demais

Bom:

{PedidoId}
{ClienteId}
{CorrelationId}
{TraceId}
{ElapsedMilliseconds}

Ruim:

{Id}
{Valor}
{Tmp}

Logging de exceções

Sempre que houver exceção, passe a exceção como argumento próprio.

Correto:

catch (Exception ex)
{
_logger.LogError(ex, "Erro ao processar pedido {PedidoId}", pedidoId);
}

Errado:

catch (Exception ex)
{
_logger.LogError("Erro: {Mensagem}", ex.Message);
}

No formato correto, o provider consegue preservar stack trace, tipo da exceção e contexto.


EventId

EventId é útil para padronizar eventos relevantes.

private static readonly EventId PedidoCriadoEvent =
new(1001, nameof(PedidoCriadoEvent));

_logger.LogInformation(PedidoCriadoEvent, "Pedido {PedidoId} criado", pedidoId);

Exemplo com enum:

public enum PedidoLogEvents
{
PedidoCriado = 1001,
PedidoCancelado = 1002,
PagamentoFalhou = 1003
}

Uso:

_logger.LogWarning(
new EventId((int)PedidoLogEvents.PagamentoFalhou, nameof(PedidoLogEvents.PagamentoFalhou)),
"Falha no pagamento do pedido {PedidoId}",
pedidoId);

Scopes

BeginScope() cria um escopo lógico que enriquece todos os logs emitidos dentro daquele bloco.

Scope com template

using (_logger.BeginScope("PedidoId: {PedidoId}", pedidoId))
{
_logger.LogInformation("Iniciando processamento");
_logger.LogInformation("Processando pagamento");
_logger.LogInformation("Finalizando processamento");
}

Scope com dicionário

using (_logger.BeginScope(new Dictionary<string, object>
{
["PedidoId"] = pedidoId,
["ClienteId"] = clienteId,
["CorrelationId"] = correlationId
}))
{
_logger.LogInformation("Processando pedido");
}

Scopes são muito úteis para:

  • correlação de logs
  • rastreabilidade por operação
  • propagação de contexto técnico
  • observabilidade

IncludeScopes

Para que os scopes apareçam em determinados providers, é comum habilitar IncludeScopes.

Exemplo:

{
"Logging": {
"Console": {
"IncludeScopes": true
}
}
}

Isso é especialmente útil no console e em pipelines de observabilidade. A documentação oficial do ASP.NET Core expõe a configuração de providers e opções como IncludeScopes. (Microsoft Learn)


Activity e correlação com tracing

O ecossistema .NET integra logging com System.Diagnostics.Activity, permitindo correlação com tracing distribuído.

Os campos mais comuns são:

  • TraceId
  • SpanId
  • ParentId
  • Tags
  • Baggage

Configuração:

builder.Logging.Configure(options =>
{
options.ActivityTrackingOptions =
ActivityTrackingOptions.TraceId |
ActivityTrackingOptions.SpanId |
ActivityTrackingOptions.ParentId |
ActivityTrackingOptions.Tags |
ActivityTrackingOptions.Baggage;
});

Isso é importante para:

  • correlação entre logs e traces
  • depuração de fluxos distribuídos
  • integração com OpenTelemetry
  • observabilidade em microserviços

A documentação oficial de logging do ASP.NET Core trata ActivityTrackingOptions como parte da configuração do logging. (Microsoft Learn)


IsEnabled

IsEnabled permite verificar se um nível está ativo antes de montar contexto caro.

if (_logger.IsEnabled(LogLevel.Debug))
{
var diagnostico = MontarDiagnosticoPesado();
_logger.LogDebug("Diagnóstico: {@Diagnostico}", diagnostico);
}

Use isso quando:

  • a montagem do log custa CPU
  • envolve serialização
  • depende de leitura cara de estado
  • ocorre em hot path

Filtros de logging

O pipeline filtra logs antes de enviá-los para os providers.

Você pode filtrar por:

  • nível
  • categoria
  • provider
  • combinação de provider + categoria

Filtro global via appsettings.json

{
"Logging": {
"LogLevel": {
"Default": "Warning",
"MinhaApp": "Information",
"MinhaApp.Services.Pagamentos": "Debug",
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

Leitura prática:

  • tudo começa em Warning
  • sua app sobe para Information
  • uma área específica sobe para Debug
  • logs do framework ficam mais silenciosos

A filtragem por categoria é parte central da configuração oficial do ASP.NET Core. (Microsoft Learn)


Filtro por provider via appsettings.json

{
"Logging": {
"LogLevel": {
"Default": "Warning"
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
},
"IncludeScopes": true
},
"Debug": {
"LogLevel": {
"Default": "Debug"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
}
}

Isso é útil quando você quer mais detalhe em um destino e menos em outro.


Filtro em código

Por categoria

builder.Logging.AddFilter("Microsoft", LogLevel.Warning);
builder.Logging.AddFilter("MinhaApp.Services", LogLevel.Information);

Por provider e categoria

builder.Logging.AddFilter<ConsoleLoggerProvider>("MinhaApp", LogLevel.Debug);
builder.Logging.AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Warning);

Filtro customizado

builder.Logging.AddFilter((provider, category, level) =>
{
if (provider.Contains("Console") && category.StartsWith("MinhaApp.Pagamentos"))
return level >= LogLevel.Debug;

if (category.StartsWith("Microsoft"))
return level >= LogLevel.Warning;

return level >= LogLevel.Information;
});

Providers

ILogger é abstração. Quem envia o log para um destino concreto é o provider.

O ASP.NET Core tem providers embutidos e também suporta providers de terceiros. Os providers built-in listados na documentação incluem Console, Debug, EventSource e EventLog no Windows. (Microsoft Learn)


Console provider

O provider de console escreve logs em stdout/stderr.

Uso:

builder.Logging.AddConsole();

É o provider mais comum em:

  • desenvolvimento
  • Docker
  • Kubernetes
  • ambientes com coleta por stdout

Exemplo de configuração:

{
"Logging": {
"Console": {
"IncludeScopes": true
},
"LogLevel": {
"Default": "Information"
}
}
}

Debug provider

Escreve logs para a saída de debug.

builder.Logging.AddDebug();

É útil principalmente em ambiente de desenvolvimento e debugging local.


EventSource provider

Escreve logs via EventSource, integrando com ferramentas de diagnóstico do ecossistema .NET.

builder.Logging.AddEventSourceLogger();

É importante em cenários com:

  • dotnet-trace
  • dotnet-monitor
  • coleta diagnóstica
  • troubleshooting em produção

EventLog provider

No Windows, é possível usar o Event Log do sistema operacional.

builder.Logging.AddEventLog();

Esse provider faz mais sentido em cenários Windows tradicionais.


Providers de terceiros

Alguns providers bastante comuns:

  • Serilog
  • NLog
  • Seq
  • Elasticsearch
  • Azure Application Insights
  • OpenTelemetry

A plataforma permite múltiplos providers simultaneamente. (Microsoft Learn)


Configuração padrão do ASP.NET Core

Ao criar a aplicação com WebApplication.CreateBuilder(args), o ASP.NET Core já configura logging com providers padrão do framework. A documentação oficial lista os providers nativos disponíveis e o pipeline padrão pode ser customizado usando ClearProviders(). (Microsoft Learn)

Exemplo:

var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.AddEventSourceLogger();

ClearProviders

ClearProviders() remove os providers previamente configurados.

builder.Logging.ClearProviders();

Use quando quiser:

  • controle total do pipeline
  • reduzir ruído
  • configurar apenas destinos específicos
  • substituir o pipeline padrão

Logging com DI

A forma mais comum é injetar ILogger<T> no construtor.

public class PagamentoService
{
private readonly ILogger<PagamentoService> _logger;

public PagamentoService(ILogger<PagamentoService> logger)
{
_logger = logger;
}
}

Em aplicações ASP.NET Core, isso funciona naturalmente com o container padrão.


Logging em Minimal APIs

app.MapPost("/pedidos", (CriarPedidoRequest request, ILoggerFactory loggerFactory) =>
{
var logger = loggerFactory.CreateLogger("PedidosEndpoint");

logger.LogInformation("Criando pedido para o cliente {ClienteId}", request.ClienteId);

return Results.Accepted();
});

Logging em middleware

app.Use(async (context, next) =>
{
var logger = context.RequestServices
.GetRequiredService<ILoggerFactory>()
.CreateLogger("RequestMiddleware");

logger.LogInformation(
"Recebendo {Method} {Path}",
context.Request.Method,
context.Request.Path);

await next();

logger.LogInformation(
"Resposta concluída com status {StatusCode}",
context.Response.StatusCode);
});

Logging em background services

public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;

public Worker(ILogger<Worker> logger)
{
_logger = logger;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker executando em {Time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}

High-performance logging

A stack oficial de logging do ASP.NET Core foi desenhada para alto desempenho, mas ainda assim existem práticas melhores para cenários de alto volume. A orientação oficial para bibliotecas recomenda preferir logging gerado por source generator e evitar interpolação de string em logs. (Microsoft Learn)

Evite interpolação e concatenação

Ruim:

_logger.LogInformation($"Pedido {pedidoId} criado");
_logger.LogInformation("Pedido " + pedidoId + " criado");

Melhor:

_logger.LogInformation("Pedido {PedidoId} criado", pedidoId);

LoggerMessage

Para hot paths, o modelo recomendado é LoggerMessage.

Exemplo com source generator

public static partial class PedidoLogs
{
[LoggerMessage(
EventId = 1001,
Level = LogLevel.Information,
Message = "Pedido {PedidoId} criado com sucesso")]
public static partial void PedidoCriado(ILogger logger, Guid pedidoId);
}

Uso:

PedidoLogs.PedidoCriado(_logger, pedidoId);

Vantagens:

  • menos alocação
  • menor overhead
  • melhor performance em alto volume
  • mais consistência

A orientação oficial de bibliotecas do .NET recomenda preferir logs gerados por source generator em vez de abordagens menos eficientes. (Microsoft Learn)


Quando usar LoggerMessage

Use quando:

  • o ponto é hot path
  • a aplicação tem altíssimo volume
  • você precisa controlar alocação
  • a latência importa muito

Não é obrigatório para todo log. Em muitos casos, LogInformation normal é suficiente.


NullLogger

NullLogger é um logger no-op, ou seja, não registra nada.

ILogger logger = NullLogger.Instance;

Versão tipada:

ILogger<PagamentoService> logger = NullLogger<PagamentoService>.Instance;

Ele é útil quando você precisa fornecer um ILogger, mas não quer emitir logs reais. A orientação oficial para autores de bibliotecas lista NullLogger.Instance e NullLogger<T> como defaults no-op suportados pela plataforma. (Microsoft Learn)


NullLoggerFactory

NullLoggerFactory é uma fábrica que cria NullLogger.

ILoggerFactory loggerFactory = NullLoggerFactory.Instance;
ILogger logger = loggerFactory.CreateLogger("QualquerCategoria");

É útil quando:

  • sua API exige ILoggerFactory
  • você quer fallback sem null
  • precisa de no-op logging
  • está em testes simples

A documentação da namespace Microsoft.Extensions.Logging.Abstractions descreve NullLoggerFactory como uma ILoggerFactory que cria NullLogger. (Microsoft Learn)


NullLoggerProvider

NullLoggerProvider é o provider no-op do ecossistema.

ILoggerProvider provider = NullLoggerProvider.Instance;
ILogger logger = provider.CreateLogger("Pagamentos");

Ele implementa ILoggerProvider, mas gera loggers que não escrevem nada.

Use quando:

  • você está no nível de provider
  • quer pipeline no-op
  • precisa compor infraestrutura sem saída real
  • quer evitar dependência em provider real

A orientação oficial de bibliotecas lista explicitamente NullLoggerProvider.Instance como um dos defaults no-op suportados. A documentação da namespace também o descreve como provider do NullLogger. (Microsoft Learn)


Diferença entre NullLogger, NullLoggerFactory e NullLoggerProvider

TipoImplementaUso comum
NullLoggerILoggerquando precisa de um logger único sem saída
NullLogger<T>ILogger<T>mesmo caso, mas com categoria tipada
NullLoggerFactoryILoggerFactoryquando precisa criar loggers dinamicamente
NullLoggerProviderILoggerProviderquando está configurando providers ou infraestrutura

Exemplo prático com fallback para NullLogger

public class PagamentoService
{
private readonly ILogger<PagamentoService> _logger;

public PagamentoService(ILogger<PagamentoService>? logger = null)
{
_logger = logger ?? NullLogger<PagamentoService>.Instance;
}

public void Processar(Guid pedidoId)
{
_logger.LogInformation("Processando pagamento do pedido {PedidoId}", pedidoId);
}
}

Esse padrão pode ser útil em bibliotecas ou testes, mas em aplicação normal com DI o ideal é receber o logger do container.


Quando usar no-op logging

Use NullLogger* quando:

  • o logging é opcional
  • você quer evitar null
  • está testando classe isolada
  • quer comportamento sem saída real

Evite NullLogger* quando:

  • você precisa observar comportamento real
  • depende de logs para operação
  • precisa correlacionar com tracing
  • o problema verdadeiro é pipeline mal configurado

OpenTelemetry

OpenTelemetry é um framework aberto e agnóstico a fornecedor para geração, coleta e exportação de telemetria como logs, métricas e traces. A documentação oficial do OpenTelemetry o define dessa forma, e o .NET integra logs via provider específico para ILogger. (OpenTelemetry)


AddOpenTelemetry no logging

Exemplo básico:

using OpenTelemetry.Logs;

builder.Logging.ClearProviders();

builder.Logging.AddOpenTelemetry(options =>
{
options.IncludeScopes = true;
options.IncludeFormattedMessage = true;
options.ParseStateValues = true;
});

Opções relevantes:

  • IncludeScopes: inclui scopes no export
  • IncludeFormattedMessage: inclui mensagem renderizada
  • ParseStateValues: preserva estado estruturado

Exemplo com OTLP

using OpenTelemetry.Logs;
using OpenTelemetry.Resources;

builder.Logging.ClearProviders();

builder.Logging.AddOpenTelemetry(options =>
{
options.IncludeScopes = true;
options.IncludeFormattedMessage = true;
options.ParseStateValues = true;

options.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService("pedidos-api"));

options.AddOtlpExporter();
});

Esse modelo é comum quando você exporta logs para collector ou backend de observabilidade.


Exemplo completo de configuração prática

using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;

var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();

builder.Logging.Configure(options =>
{
options.ActivityTrackingOptions =
ActivityTrackingOptions.TraceId |
ActivityTrackingOptions.SpanId |
ActivityTrackingOptions.ParentId |
ActivityTrackingOptions.Tags |
ActivityTrackingOptions.Baggage;
});

builder.Logging.AddConsole(options =>
{
options.IncludeScopes = true;
});

builder.Logging.AddDebug();
builder.Logging.AddEventSourceLogger();

builder.Logging.AddFilter("Microsoft", LogLevel.Warning);
builder.Logging.AddFilter("Microsoft.AspNetCore", LogLevel.Warning);
builder.Logging.AddFilter("MinhaApp", LogLevel.Information);
builder.Logging.AddFilter("MinhaApp.Pagamentos", LogLevel.Debug);

builder.Logging.AddOpenTelemetry(options =>
{
options.IncludeScopes = true;
options.IncludeFormattedMessage = true;
options.ParseStateValues = true;

options.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService("minha-api"));

options.AddOtlpExporter();
});

var app = builder.Build();

app.MapGet("/", (ILoggerFactory loggerFactory) =>
{
var logger = loggerFactory.CreateLogger("Startup");

using (logger.BeginScope(new Dictionary<string, object>
{
["CorrelationId"] = Guid.NewGuid()
}))
{
logger.LogInformation("Aplicação iniciada");
}

return Results.Ok();
});

app.Run();

Exemplo prático em service

public class PedidoService
{
private readonly ILogger<PedidoService> _logger;

private static readonly EventId PedidoProcessadoEvent =
new(2001, nameof(PedidoProcessadoEvent));

public PedidoService(ILogger<PedidoService> logger)
{
_logger = logger;
}

public async Task ProcessarAsync(Guid pedidoId, Guid clienteId, CancellationToken ct)
{
using (_logger.BeginScope(new Dictionary<string, object>
{
["PedidoId"] = pedidoId,
["ClienteId"] = clienteId
}))
{
_logger.LogInformation("Iniciando processamento do pedido");

try
{
await Task.Delay(100, ct);

_logger.LogInformation(
PedidoProcessadoEvent,
"Pedido {PedidoId} processado com sucesso",
pedidoId);
}
catch (OperationCanceledException)
{
_logger.LogWarning("Processamento cancelado");
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Erro ao processar pedido {PedidoId}", pedidoId);
throw;
}
}
}
}

Configuração sugerida para Development

{
"Logging": {
"LogLevel": {
"Default": "Information",
"MinhaApp": "Debug",
"Microsoft": "Warning"
},
"Console": {
"IncludeScopes": true
}
}
}

Boa prática:

  • mais detalhe da aplicação
  • menos ruído do framework
  • scopes visíveis
  • Debug só onde fizer sentido

Configuração sugerida para Produção

{
"Logging": {
"LogLevel": {
"Default": "Warning",
"MinhaApp": "Information",
"MinhaApp.Pagamentos": "Information",
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning"
},
"Console": {
"IncludeScopes": true
}
}
}

Boa prática:

  • evitar Debug global
  • manter a app em Information
  • framework em Warning
  • preservar correlação
  • exportar para backend centralizado

Logging de dados sensíveis

Nem todo dado deve ir para log.

Evite registrar:

  • senha
  • token
  • JWT completo
  • segredo de configuração
  • dados bancários
  • PII sem critério
  • payload bruto inteiro sem necessidade

Melhor:

_logger.LogInformation(
"Callback recebido. Pedido: {PedidoId}, Valor: {Valor}, PayloadSize: {PayloadSize}",
pedidoId,
valor,
payload.Length);

Erros comuns

Concatenar string

_logger.LogInformation("Pedido " + pedidoId + " criado");

Usar interpolação sem necessidade em hot path

_logger.LogInformation($"Pedido {pedidoId} criado");

Passar só ex.Message

_logger.LogError(ex.Message);

Não usar scopes em operações correlacionadas

Deixar tudo em Debug em produção

Não filtrar Microsoft.*

Logar segredo

Nomear propriedades mal

Ruim:

_logger.LogInformation("Valor {Id}", pedidoId);

Melhor:

_logger.LogInformation("Pedido {PedidoId} criado", pedidoId);

Guia rápido

SituaçãoRecomendação
Logger por classeILogger<T>
Logger dinâmicoILoggerFactory.CreateLogger()
Fallback sem logsNullLogger<T> ou NullLoggerFactory
Provider no-opNullLoggerProvider.Instance
Correlação entre logsBeginScope()
Log local e containersAddConsole()
Debug localAddDebug()
Diagnóstico .NETAddEventSourceLogger()
Windows Event LogAddEventLog()
Hot pathLoggerMessage
Observabilidade modernaAddOpenTelemetry()
Reduzir ruído do frameworkfiltros por categoria/provider

Referências

  • Logging no ASP.NET Core e .NET. (Microsoft Learn)
  • Providers built-in do ASP.NET Core. (Microsoft Learn)
  • Guidance oficial para autores de bibliotecas .NET usando ILogger, ILoggerFactory, NullLogger, NullLoggerFactory e NullLoggerProvider. (Microsoft Learn)
  • Namespace Microsoft.Extensions.Logging.Abstractions, incluindo NullLoggerFactory e NullLoggerProvider. (Microsoft Learn)
  • Definição e papel do OpenTelemetry em logs, métricas e traces. (OpenTelemetry)