Registro em log no .NET

O .NET dá suporte a uma API de registro em log que funciona com uma variedade de provedores de log integrados e de terceiros. Este artigo mostra como usar a API de registro em log com provedores internos. A maioria dos exemplos de código mostrados neste artigo se aplicam a qualquer aplicativo .NET que usa o Host Genérico. Para aplicativos que não usam o Host Genérico, consulte Aplicativo de console não host.

Criar logs

Para criar logs, use um ILogger<TCategoryName> objeto da DI (injeção de dependência).

O exemplo a seguir:

  • Cria um logger, ILogger<Worker> , que usa uma categoria de log do nome totalmente qualificado do tipo Worker . A categoria do log é uma cadeia de caracteres associada a cada log.
  • Chama LogInformation para fazer logoff no nível Information . O nível de log indica a gravidade do evento registrado.
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 running at: {time}", DateTimeOffset.UtcNow);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Níveis e categorias serão explicados com mais detalhes posteriormente neste artigo.

Configurar o registro em log

A configuração de registro em log normalmente é fornecida Logging pela seção de appsettings. {Environment} Arquivos .json. O seguinte appsettings.Development.jsno arquivo é gerado pelos modelos de serviço do .NET Worker:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

No JSON anterior:

  • As "Default" "Microsoft" categorias , "Microsoft.Hosting.Lifetime" e são especificadas.
  • A "Microsoft" categoria se aplica a todas as categorias que começam com "Microsoft" .
  • A "Microsoft" categoria registra em log no nível de log e Warning superior.
  • A categoria é mais específica do que a categoria, portanto, a categoria registra em "Microsoft.Hosting.Lifetime" log o nível de log "Microsoft" "Microsoft.Hosting.Lifetime" "Informações" e superior.
  • Um provedor de log específico não é especificado, portanto, aplica-se a todos os provedores de log habilitados, exceto para o LogLevel Windows EventLog.

A Logging propriedade pode ter as propriedades do provedor de log e LogLevel . O LogLevel especifica o nível mínimo a ser log para categorias selecionadas. No JSON anterior, Information e os níveis de log são Warning especificados. LogLevel indica a severidade do log e os intervalos de 0 a 6:

Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5 e = None 6.

Quando um LogLevel é especificado, o registro em log é habilitado para mensagens no nível especificado e superior. No JSON anterior, a Default categoria é registrada para e Information superior. Por exemplo, Information as mensagens , , e são Warning Error Critical registradas. Se nenhum LogLevel for especificado, o registro em log assume como padrão o Information nível . Para obter mais informações, consulte Níveis de log.

Uma propriedade de provedor pode especificar uma LogLevel propriedade . LogLevel em um provedor especifica os níveis a registrar nesse provedor e substitui as configurações de log que não são do provedor. Considere o seguinte appsettings.jsno arquivo:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.Hosting": "Trace"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Default": "Warning"
            }
        }
    }
}

Configurações em Logging.{ProviderName}.LogLevel configurações de substituição no Logging.LogLevel . No JSON anterior, o nível de log padrão do provedor Debug é definido como Information :

Logging:Debug:LogLevel:Default:Information

A configuração anterior especifica o nível Information de log para cada Logging:Debug: categoria, exceto Microsoft.Hosting . Quando uma categoria específica é listada, a categoria específica substitui a categoria padrão. No JSON anterior, as Logging:Debug:LogLevel categorias "Microsoft.Hosting" e "Default" substituem as configurações em Logging:LogLevel

O nível de log mínimo pode ser especificado para qualquer um dos:

  • Provedores específicos: por exemplo, Logging:EventSource:LogLevel:Default:Information
  • Categorias específicas: por exemplo, Logging:LogLevel:Microsoft:Warning
  • Todos os provedores e todas as categorias: Logging:LogLevel:Default:Warning

Todos os logs abaixo do nível mínimo não são:

  • Passado para o provedor.
  • Registrado ou exibido.

Para suprimir todos os logs, especifique LogLevel.None. LogLevel.None tem um valor de 6, que é maior que LogLevel.Critical (5).

Se um provedor dá suporte a escopos de log, indica se eles estão IncludeScopes habilitados. Para obter mais informações, consulte escopos de log

O seguinte appsettings.jsno arquivo contém configurações para todos os provedores integrados:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information"
            }
        },
        "Console": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft.Extensions.Hosting": "Warning",
                "Default": "Information"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "EventLog": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "AzureAppServicesFile": {
            "IncludeScopes": true,
            "LogLevel": {
                "Default": "Warning"
            }
        },
        "AzureAppServicesBlob": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "ApplicationInsights": {
            "LogLevel": {
                "Default": "Information"
            }
        }
    }
}

Na amostra anterior:

  • As categorias e os níveis não são valores sugeridos. O exemplo é fornecido para mostrar todos os provedores padrão.
  • Configurações em Logging.{ProviderName}.LogLevel configurações de substituição no Logging.LogLevel . Por exemplo, o nível em Debug.LogLevel.Default substitui o nível em LogLevel.Default .
  • O alias de cada provedor é usado. Cada provedor define um alias que pode ser usado na configuração no lugar do nome de tipo totalmente qualificado. Os aliases dos provedores integrados são:
    • Console
    • Depurar
    • EventSource
    • EventLog
    • AzureAppServicesFile
    • AzureAppServicesBlob
    • ApplicationInsights

Definir o nível de log por linha de comando, variáveis de ambiente e outras configurações

O nível de log pode ser definido por qualquer um dos provedores de configuração. Por exemplo, você pode criar uma variável de ambiente persistente chamada Logging:LogLevel:Microsoft com um valor de Information .

Crie e atribua uma variável de ambiente persistente, considerando o valor do nível de log.

:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M

Em uma nova instância do Prompt de Comando, leia a variável de ambiente.

:: Prints the env var value
echo %Logging__LogLevel__Microsoft%

A configuração de ambiente anterior é persistida no ambiente. Para testar as configurações ao usar um aplicativo criado com os modelos de serviço do .NET Worker, use o comando no diretório do projeto depois que a variável de ambiente dotnet run for atribuída.

dotnet run

Dica

Depois de definir uma variável de ambiente, reinicie o IDE (ambiente de desenvolvimento integrado) para garantir que as variáveis de ambiente adicionadas recentemente estejam disponíveis.

No Serviço de Aplicativo do Azure, selecione Nova configuração de aplicativo na página Configurações > Configuração. Serviço de Aplicativo do Azure configurações do aplicativo são:

  • Criptografado em repouso e transmitido por um canal criptografado.
  • Exposto como variáveis de ambiente.

Para obter mais informações sobre como definir valores de configuração do .NET usando variáveis de ambiente, consulte variáveis de ambiente.

Como as regras de filtragem são aplicadas

Quando um objeto ILogger<TCategoryName> é criado, o objeto ILoggerFactory seleciona uma única regra por provedor para aplicar a esse agente. Todas as mensagens gravadas pela instância ILogger são filtradas com base nas regras selecionadas. A regra mais específica para cada provedor e par de categorias é selecionada nas regras disponíveis.

O algoritmo a seguir é usado para cada provedor quando um ILogger é criado para uma determinada categoria:

  • Selecione todas as regras que correspondem ao provedor ou seu alias. Se nenhuma correspondência for encontrada, selecione todas as regras com um provedor vazio.
  • Do resultado da etapa anterior, selecione as regras com o prefixo de categoria de maior correspondência. Se nenhuma correspondência for encontrada, selecione todas as regras que não especificam uma categoria.
  • Se várias regras forem selecionadas, use a última.
  • Se nenhuma regra for selecionada, use para LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) especificar o nível mínimo de log.

Categoria do log

Quando um ILogger objeto é criado, uma categoria é especificada. Essa categoria é incluída em cada mensagem de log criada por essa instância de ILogger. A cadeia de caracteres de categoria é arbitrária, mas a convenção é usar o nome da classe. Por exemplo, em um aplicativo com um serviço definido como o seguinte objeto, a categoria pode ser "Example.DefaultService" :

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger<DefaultService> _logger;

        public DefaultService(ILogger<DefaultService> logger) =>
            _logger = logger;

        // ...
    }
}

Para especificar explicitamente a categoria, chame LoggerFactory.CreateLogger:

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger _logger;

        public DefaultService(ILoggerFactory loggerFactory) =>
            _logger = loggerFactory.CreateLogger("CustomCategory");

        // ...
    }
}

Chamar com um nome fixo pode ser útil quando usado em várias classes/tipos para que os eventos CreateLogger possam ser organizados por categoria.

ILogger<T> é equivalente a chamar CreateLogger com o nome de tipo totalmente qualificado de T.

Nível de log

A tabela a seguir lista LogLevel os valores, o método de Log{LogLevel} extensão de conveniência e o uso sugerido:

LogLevel Valor Método Descrição
Trace 0 LogTrace Contêm as mensagens mais detalhadas. Essas mensagens podem conter dados confidenciais do aplicativo. Essas mensagens são desabilitadas por padrão e não devem ser habilitadas em produção.
Depurar 1 LogDebug Para depuração e desenvolvimento. Use com cuidado na produção devido ao alto volume.
Informações 2 LogInformation Rastreia o fluxo geral do aplicativo. Pode ter um valor de longo prazo.
Aviso 3 LogWarning Para eventos anormais ou inesperados. Normalmente, inclui erros ou condições que não causam falha no aplicativo.
Erro 4 LogError Para erros e exceções que não podem ser manipulados. Essas mensagens indicam uma falha na operação ou solicitação atual, não uma falha em todo o aplicativo.
Crítico 5 LogCritical Para falhas que exigem atenção imediata. Exemplos: cenários de perda de dados, espaço em disco insuficiente.
Nenhuma 6 Especifica que nenhuma mensagem deve ser escrita.

Na tabela anterior, o LogLevel é listado da gravidade mais baixa para a mais alta.

O primeiro parâmetro do método Log, LogLevel , indica a severidade do log. Em vez de chamar Log(LogLevel, ...) , a maioria dos desenvolvedores chama os métodos de extensão Log{LogLevel}. Os Log{LogLevel} métodos de extensão chamam o método Log e especificam o LogLevel. Por exemplo, as duas chamadas de log a seguir são funcionalmente equivalentes e produzem o mesmo log:

public void LogDetails()
{
    var logMessage = "Details for log.";

    _logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
    _logger.LogInformation(AppLogEvents.Details, logMessage);
}

AppLogEvents.Details é a ID do evento e é implicitamente representada por um valor Int32 constante. AppLogEvents é uma classe que expõe várias constantes de identificador nomeado e é exibida na seção ID do evento de log.

O código a seguir cria os logs Information e Warning:

public async Task<T> GetAsync<T>(string id)
{
    _logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);

    var result = await _repository.GetAsync(id);
    if (result is null)
    {
        _logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
    }

    return result;
}

No código anterior, o primeiro Log{LogLevel} parâmetro, AppLogEvents.Read , é a ID do evento de log. O segundo parâmetro é um modelo de mensagem com espaços reservados para valores de argumento fornecidos pelos parâmetros de método restantes. Os parâmetros de método são explicados na seção modelo de mensagem mais adiante neste artigo.

Configure o nível de log apropriado e chame os métodos corretos para controlar a quantas saídas de log são Log{LogLevel} gravados em uma mídia de armazenamento específica. Por exemplo:

  • Em produção:
    • O registro em Trace log nos níveis ou produz um alto volume de mensagens de log Information detalhadas. Para controlar os custos e não exceder os limites de armazenamento de dados, registre e nivele mensagens para um armazenamento de dados de alto Trace Information volume e baixo custo. Considere limitar Trace e Information a categorias específicas.
    • O registro em Warning log em níveis deve produzir poucas mensagens de Critical log.
      • Os custos e os limites de armazenamento geralmente não são uma preocupação.
      • Alguns logs permitem mais flexibilidade nas opções de armazenamento de dados.
  • Em desenvolvimento:
    • Defina como Warning.
    • Adicionar Trace ou mensagens ao solucionar Information problemas. Para limitar a saída, Trace de definido ou apenas para as Information categorias em investigação.

O seguinte JSON define Logging:Console:LogLevel:Microsoft:Information :

{
    "Logging": {
        "LogLevel": {
            "Microsoft": "Warning"
        },
        "Console": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        }
    }
}

ID de evento de log

Cada log pode especificar um identificador de evento, o é uma EventId estrutura com propriedades readonly e Id Name opcionais. O código-fonte de exemplo usa AppLogEvents a classe para definir IDs de evento:

internal static class AppLogEvents
{
    internal const int Create = 1000;
    internal const int Read = 1001;
    internal const int Update = 1002;
    internal const int Delete = 1003;

    internal const int Details = 3000;
    internal const int Error = 3001;

    internal const int ReadNotFound = 4000;
    internal const int UpdateNotFound = 4001;

    // ...
}

Uma ID de evento associa um conjunto de eventos. Por exemplo, todos os logs relacionados à leitura de valores de um repositório podem ser 1001 .

O provedor de log pode registrar a ID do evento em um campo de ID, na mensagem de log ou não. O provedor de Depuração não mostra IDs de eventos. O provedor de console mostra IDs de evento entre colchetes após a categoria:

info: Example.DefaultService.GetAsync[1001]
      Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
      GetAsync(a1b2c3) not found

Alguns provedores de log armazenam a ID do evento em um campo, o que permite a filtragem na ID.

Modelo de mensagem de log

Cada API de log usa um modelo de mensagem. O modelo de mensagem pode conter espaços reservados para os quais são fornecidos argumentos. Use nomes para os espaços reservados, não números. A ordem dos espaços reservados e não de seus nomes, determina quais parâmetros serão usados para fornecer seus valores. No código a seguir, os nomes de parâmetro estão fora de sequência no modelo de mensagem:

string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);

O código anterior cria uma mensagem de log com os valores de parâmetro em sequência:

Parameter values: param1, param2

Essa abordagem permite que os provedores de log implementem o registro em log semântico ou estruturado. Os próprios argumentos são passados para o sistema de registro em log, não apenas o modelo de mensagem formatado. Isso permite que provedores de log armazenem os valores de parâmetro como campos. Considere o seguinte método de logger:

_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);

Por exemplo, ao registrar em log no Azure Table Armazenamento:

  • Cada entidade tabela do Azure pode ter ID propriedades RunTime e .
  • Tabelas com propriedades simplificam consultas em dados registrados. Por exemplo, uma consulta pode encontrar todos os logs dentro de um intervalo específico sem precisar analisar o tempo fora RunTime da mensagem de texto.

Registrar exceções em log

Os métodos de logger têm sobrecargas que levam um parâmetro de exceção:

public void Test(string id)
{
    try
    {
        if (id == "none")
        {
            throw new Exception("Default Id detected.");
        }
    }
    catch (Exception ex)
    {
        _logger.LogWarning(
            AppLogEvents.Error, ex,
            "Failed to process iteration: {Id}", id);
    }
}

O log de exceção é específico do provedor.

Nível de log padrão

Se o nível de log padrão não estiver definido, o valor padrão do nível de log será Information .

Por exemplo, considere o seguinte aplicativo de serviço de trabalho:

  • Criado com os modelos do .NET Worker.
  • appsettings.jsem e appsettings.Development.jsexcluídos ou renomeados.

Com a configuração anterior, navegar até a privacidade ou home page produz muitas Trace Debug mensagens, e Information com Microsoft no nome da categoria.

O código a seguir define o nível de log padrão quando o nível de log padrão não está definido na configuração:

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning));
}

Função Filter

Uma função de filtro é invocada para todos os provedores e categorias que não têm regras atribuídas a eles por configuração ou código:

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
                logging.AddFilter((provider, category, logLevel) =>
                {
                    return provider.Contains("ConsoleLoggerProvider")
                        && (category.Contains("Example") || category.Contains("Microsoft"))
                        && logLevel >= LogLevel.Information;
                }));
}

O código anterior exibe os logs do console quando a categoria contém Example ou Microsoft e o nível de log é Information ou superior.

Escopos de log

Um escopo pode agrupar um conjunto de operações lógicas. Esse agrupamento pode ser usado para anexar os mesmos dados para cada log criado como parte de um conjunto. Por exemplo, todo log criado como parte do processamento de uma transação pode incluir a ID da transação.

Um escopo:

Os seguintes provedores dão suporte a escopos:

Use um escopo por meio do encapsulamento de chamadas de agente em um bloco using:

public async Task<T> GetAsync<T>(string id)
{
    T result;

    using (_logger.BeginScope("using block message"))
    {
        _logger.LogInformation(
            AppLogEvents.Read, "Reading value for {Id}", id);

        var result = await _repository.GetAsync(id);
        if (result is null)
        {
            _logger.LogWarning(
                AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
        }
    }

    return result;
}

O JSON a seguir habilita os escopos para o provedor de console:

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

O código a seguir habilita os escopos para o provedor de console:

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging((_, logging) =>
                logging.ClearProviders()
                    .AddConsole(options => options.IncludeScopes = true));
}

Aplicativo de console não host

O código de log para aplicativos sem um host genérico difere na maneira como os provedores são adicionados e os agentes são criados. Em um aplicativo de console não host, chame o método de extensão Add{provider name} do provedor ao criar um LoggerFactory:

class Program
{
    static void Main(string[] args)
    {
        using var loggerFactory = LoggerFactory.Create(builder =>
        {
            builder
                .AddFilter("Microsoft", LogLevel.Warning)
                .AddFilter("System", LogLevel.Warning)
                .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
                .AddConsole();
        });

        ILogger logger = loggerFactory.CreateLogger<Program>();
        logger.LogInformation("Example log message");
    }
}

O loggerFactory objeto é usado para criar uma ILogger instância.

Criar logs no principal

O código a seguir faz logon Main obtendo uma ILogger instância de di após a criação do host:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

class Program
{
    static Task Main(string[] args)
    {
        IHost host = Host.CreateDefaultBuilder(args).Build();

        var logger = host.Services.GetRequiredService<ILogger<Program>>();
        logger.LogInformation("Host created.");

        return host.RunAsync();
    }
}

Sem métodos de agente assíncronos

O registro em log deve ser tão rápido que não justifique o custo de desempenho de código assíncrono. Se um armazenamento de dados de log estiver lento, não grave-o diretamente. Considere gravar as mensagens de log em um repositório rápido inicialmente e, em seguida, movê-las para o armazenamento lento mais tarde. por exemplo, ao fazer logon no SQL Server, não faça isso diretamente em um Log método, pois os Log métodos são síncronos. Em vez disso, adicione mensagens de log de forma síncrona a uma fila na memória e faça com que uma função de trabalho de plano de fundo efetue pull das mensagens para fora da fila para fazer o trabalho assíncrono de envio de dados por push para o SQL Server.

Alterar os níveis de log em um aplicativo em execução

A API de registro em log não inclui um cenário para alterar os níveis de log enquanto um aplicativo está em execução. No entanto, alguns provedores de configuração são capazes de recarregar a configuração, o que exige um efeito imediato na configuração de log. Por exemplo, o provedor de configuração de arquivo recarrega a configuração de log por padrão. Se a configuração for alterada no código enquanto um aplicativo estiver em execução, o aplicativo poderá chamar IConfigurationRoot. recarregar para atualizar a configuração de log do aplicativo.

Pacotes NuGet

As ILogger<TCategoryName> ILoggerFactory interfaces e implementações são incluídas no SDK do .NET Core. eles também estão disponíveis nos seguintes pacotes de NuGet:

Aplicar regras de filtro de log no código

A abordagem preferida para definir regras de filtro de log é usando a configuraçãodo.

O exemplo a seguir mostra como registrar regras de filtro no código:

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
               logging.AddFilter("System", LogLevel.Debug)
                  .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information)
                  .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace));
}

logging.AddFilter("System", LogLevel.Debug) Especifica a System categoria e o nível de log Debug . O filtro é aplicado a todos os provedores porque um provedor específico não foi configurado.

AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information) especificado

  • O Debug provedor de log.
  • Nível Information de log e superior.
  • Todas as categorias que começam com "Microsoft" .

Confira também