Efetuando login em C# e .NET

O .NET oferece suporte ao log estruturado de alto desempenho por meio da API para ajudar a monitorar o comportamento do ILogger aplicativo e diagnosticar problemas. Os logs podem ser gravados em destinos diferentes configurando diferentes provedores de log. Os provedores básicos de registro em log são internos e há muitos provedores de terceiros disponíveis também.

Começar agora

Este primeiro exemplo mostra o básico, mas só é adequado para um aplicativo de console trivial. Este aplicativo de console de exemplo depende dos seguintes pacotes NuGet:

Na próxima seção, você verá como melhorar o código considerando escala, desempenho, configuração e padrões típicos de programação.

using Microsoft.Extensions.Logging;

using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

O exemplo anterior:

  • Cria um ILoggerFactoryarquivo . O ILoggerFactory armazena toda a configuração que determina para onde as mensagens de log são enviadas. Nesse caso, você configura o provedor de log do console para que as mensagens de log sejam gravadas no console.
  • Cria um ILogger com uma categoria chamada "Programa". A categoria é uma string que está associada a cada mensagem registrada pelo ILogger objeto. Ele é usado para agrupar mensagens de log da mesma classe (ou categoria) ao pesquisar ou filtrar logs.
  • Chamadas LogInformation para registrar uma mensagem no Information nível. O nível de log indica a gravidade do evento registrado e é usado para filtrar mensagens de log menos importantes. A entrada de log também inclui um modelo "Hello World! Logging is {Description}." de mensagem e um par Description = funchave-valor. O nome da chave (ou espaço reservado) vem da palavra dentro das chaves no modelo e o valor vem do argumento de método restante.

Este arquivo de projeto para este exemplo inclui dois pacotes NuGet:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
  </ItemGroup>

</Project>

Gorjeta

Todo o código-fonte de exemplo de registro está disponível no Navegador de amostras para download. Para obter mais informações, consulte Procurar exemplos de código: fazendo login no .NET.

Iniciar sessão numa aplicação não trivial

Há várias alterações que você deve considerar fazer no exemplo anterior ao fazer login em um cenário menos trivial:

  • Se seu aplicativo estiver usando DI (Injeção de Dependência) ou um host como o ASP. NET's WebApplication ou Host Genérico, então você deve usar ILoggerFactory e ILogger objetos de seus respetivos contêineres DI em vez de criá-los diretamente. Para obter mais informações, consulte Integração com DI e hosts.

  • Registrar a geração de código-fonte em tempo de compilação geralmente é uma alternativa melhor para métodos de ILogger extensão como LogInformation. A geração de origem de registro em log oferece melhor desempenho, digitação mais forte e evita espalhar string constantes por todos os métodos. A contrapartida é que usar essa técnica requer um pouco mais de código.

using Microsoft.Extensions.Logging;

internal partial class Program
{
    static void Main(string[] args)
    {
        using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
        ILogger logger = factory.CreateLogger("Program");
        LogStartupMessage(logger, "fun");
    }

    [LoggerMessage(Level = LogLevel.Information, Message = "Hello World! Logging is {Description}.")]
    static partial void LogStartupMessage(ILogger logger, string description);
}
  • A prática recomendada para nomes de categoria de log é usar o nome totalmente qualificado da classe que está criando a mensagem de log. Isso ajuda a relacionar as mensagens de log com o código que as produziu e oferece um bom nível de controle ao filtrar logs. CreateLogger aceita um Type para tornar esta nomenclatura fácil de fazer.
using Microsoft.Extensions.Logging;

internal class Program
{
    static void Main(string[] args)
    {
        using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
        ILogger logger = factory.CreateLogger<Program>();
        logger.LogInformation("Hello World! Logging is {Description}.", "fun");
    }
}
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;

using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
    builder.AddOpenTelemetry(logging =>
    {
        logging.AddOtlpExporter();
    });
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

Integração com hosts e injeção de dependência

Se seu aplicativo estiver usando DI (Injeção de Dependência) ou um host como o ASP. NET's WebApplication ou Host Genérico, então você deve usar ILoggerFactory e ILogger objetos do contêiner DI em vez de criá-los diretamente.

Obtenha um ILogger da DI

Este exemplo obtém um objeto ILogger em um aplicativo hospedado usando ASP.NET APIs mínimas:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<ExampleHandler>();

var app = builder.Build();

var handler = app.Services.GetRequiredService<ExampleHandler>();
app.MapGet("/", handler.HandleRequest);

app.Run();

partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
    public string HandleRequest()
    {
        LogHandleRequest(logger);
        return "Hello World";
    }

    [LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
    public static partial void LogHandleRequest(ILogger logger);
}

O exemplo anterior:

  • Criou um serviço singleton chamado ExampleHandler e mapeou solicitações da Web de entrada para executar a ExampleHandler.HandleRequest função.
  • A linha 8 define um construtor primário para o ExampleHandler, um recurso adicionado em C# 12. Usar o construtor C# de estilo mais antigo funcionaria igualmente bem, mas é um pouco mais detalhado.
  • O construtor define um parâmetro do tipo ILogger<ExampleHandler>. ILogger<TCategoryName> deriva e ILogger indica qual categoria o ILogger objeto tem. O contêiner DI localiza um ILogger com a categoria correta e o fornece como o argumento do construtor. Se ainda não ILogger existir essa categoria, o contêiner DI a criará automaticamente a ILoggerFactory partir do no provedor de serviços.
  • O logger parâmetro recebido no construtor foi usado para registrar na HandleRequest função.

ILoggerFactory fornecida pelo host

Os construtores de hosts inicializam a configuração padrão e, em seguida, adicionam um objeto configurado ILoggerFactory ao contêiner DI do host quando o host é construído. Antes que o host seja criado, você pode ajustar a configuração de log por meio de HostApplicationBuilder.Logging, WebApplicationBuilder.Loggingou APIs semelhantes em outros hosts. Os hosts também aplicam a configuração de log de fontes de configuração padrão como variáveis de appsettings.json e ambiente. Para obter mais informações, consulte Configuração no .NET.

Este exemplo expande o anterior para personalizar o ILoggerFactory fornecido pelo WebApplicationBuilder. Ele adiciona OpenTelemetry como um provedor de log transmitindo os logs por OTLP (protocolo OpenTelemetry ):

var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();

Criar um ILoggerFactory com DI

Se você estiver usando um contêiner DI sem um host, use AddLogging para configurar e adicionar ILoggerFactory ao contêiner.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

// Add services to the container including logging
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();

// Get the ExampleService object from the container
ExampleService service = serviceProvider.GetRequiredService<ExampleService>();

// Do some pretend work
service.DoSomeWork(10, 20);

class ExampleService(ILogger<ExampleService> logger)
{
    public void DoSomeWork(int x, int y)
    {
        logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
    }
}

O exemplo anterior:

  • Criado um contêiner de serviço DI contendo um ILoggerFactory configurado para gravar no console
  • Adicionado um singleton ExampleService ao contêiner
  • Criou uma instância do ExampleService contêiner from the DI que também criou automaticamente um ILogger<ExampleService> para usar como o argumento do construtor.
  • Invocado ExampleService.DoSomeWork que usou o ILogger<ExampleService> para registrar uma mensagem no console.

Configurar registo

A configuração de registro em log é definida em código ou por meio de fontes externas, como arquivos de configuração e variáveis de ambiente. O uso da configuração externa é benéfico quando possível, pois pode ser alterado sem reconstruir o aplicativo. No entanto, algumas tarefas, como definir provedores de log, só podem ser configuradas a partir do código.

Configurar o registro em log sem código

Para aplicativos que usam um host, a "Logging" configuração de log geralmente é fornecida pela seção de appsettings.{Environment}.json arquivos. Para aplicativos que não usam um host, as fontes de configuração externas são configuradas explicitamente ou configuradas em código .

As seguintes configurações de aplicativo. Development.json 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"categorias , "Microsoft", e "Microsoft.Hosting.Lifetime" nível de log são especificadas.
  • O "Default" valor é aplicado a todas as categorias que não são especificadas de outra forma, efetivamente tornando todos os valores padrão para todas as categorias "Information". Você pode substituir esse comportamento especificando um valor para uma categoria.
  • A "Microsoft" categoria aplica-se a todas as categorias que começam com "Microsoft".
  • A "Microsoft" categoria registra em um nível de log de Warning e superior.
  • A "Microsoft.Hosting.Lifetime" categoria é mais específica do que a "Microsoft" categoria, portanto, a "Microsoft.Hosting.Lifetime" categoria registra no nível "Information" de log e superior.
  • Um provedor de log específico não é especificado, portantoLogLevel, aplica-se a todos os provedores de log habilitados, exceto para o Log de Eventos do Windows.

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

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

Quando a é especificada, o LogLevel registro em log é habilitado para mensagens no nível especificado e superior. No JSON anterior, a Default categoria é registrada para Information e superior. Por exemplo, Information, Warning, Error, e Critical as mensagens são registradas. Se não LogLevel for especificado, o registro em log será padronizado para 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 serem registrados para esse provedor e substitui as configurações de log que não são do provedor. Considere o seguinte arquivo appsettings.json :

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

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

Logging:Debug:LogLevel:Default:Information

A configuração anterior especifica o nível de Information 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 mínimo de log pode ser especificado para:

  • Fornecedores específicos: Por exemplo, Logging:EventSource:LogLevel:Default:Information
  • Categorias específicas: Por exemplo, Logging:LogLevel:Microsoft:Warning
  • Todos os fornecedores e todas as categorias: Logging:LogLevel:Default:Warning

Quaisquer 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 é superior a LogLevel.Critical (5).

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

O seguinte ficheiro appsettings.json contém definições para todos os fornecedores incorporados:

{
    "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 níveis não são valores sugeridos. O exemplo é fornecido para mostrar todos os provedores padrão.
  • Configurações em Logging.{ProviderName}.LogLevel substituir configurações em 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 do tipo totalmente qualificado. Os aliases dos provedores internos são:
    • Console
    • Debug
    • 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 nomeada Logging:LogLevel:Microsoft com um valor de Information.

Crie e atribua variável de ambiente persistente, dado o valor de 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 é mantida no ambiente. Para testar as configurações ao usar um aplicativo criado com os modelos de serviço do .NET Worker, use o dotnet run comando no diretório do projeto depois que a variável de ambiente for atribuída.

dotnet run

Gorjeta

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

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

  • Encriptado em repouso e transmitido através de um canal encriptado.
  • 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.

Configurar o registro em log com código

Para configurar o código de login, use a ILoggingBuilder API. Este pode ser acedido a partir de diferentes locais:

Este exemplo mostra a configuração do provedor de log do console e vários filtros.

using Microsoft.Extensions.Logging;

using var loggerFactory = LoggerFactory.Create(static builder =>
{
    builder
        .AddFilter("Microsoft", LogLevel.Warning)
        .AddFilter("System", LogLevel.Warning)
        .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
        .AddConsole();
});

ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogDebug("Hello {Target}", "Everyone");

No exemplo AddFilter anterior é usado para ajustar o nível de log habilitado para várias categorias. AddConsole é usado para adicionar o provedor de log do console. Por padrão, os logs com Debug gravidade não são habilitados, mas como a configuração ajustou os filtros, a mensagem de depuração "Olá a todos" é exibida no console.

Como as regras de filtragem são aplicadas

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

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

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

Categoria do registo

Quando um ILogger objeto é criado, uma categoria é especificada. Essa categoria é incluída em cada mensagem de log criada por essa instância do ILogger. A cadeia de caracteres de categoria é arbitrária, mas a convenção é usar o nome de classe totalmente qualificado. 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;

        // ...
    }
}

Se desejar uma categorização adicional, a convenção é usar um nome hierárquico anexando uma subcategoria ao nome de classe totalmente qualificado e especificar explicitamente a categoria usando LoggerFactory.CreateLogger:

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

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

        // ...
    }
}

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

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

Nível de registo

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

Nível de Registo Value Método Description
Rastreio 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 na produção.
Debug 1 LogDebug Para depuração e desenvolvimento. Use com cautela na produção devido ao alto volume.
Informações 2 LogInformation Rastreia o fluxo geral do aplicativo. Pode ter valor a longo prazo.
Aviso 3 LogWarning Para eventos anormais ou inesperados. Normalmente, inclui erros ou condições que não fazem com que o aplicativo falhe.
Erro 4 LogError Para erros e exceções que não podem ser tratados. 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, sem espaço em disco.
Nenhuma 6 Especifica que nenhuma mensagem deve ser gravada.

Na tabela anterior, o LogLevel é listado da menor para a maior gravidade.

O primeiro parâmetro do método Log , LogLevel, indica a gravidade 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 Log método 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 constante Int32 . AppLogEventsé uma classe que expõe várias constantes de identificador nomeado e é exibida na seção ID do evento Log.

O código a seguir cria Information e Warning registra:

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 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 do método são explicados na seção de modelo de mensagem mais adiante neste artigo.

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

  • Em produção:
    • O registro em log nos Trace níveis ou Debug produz um grande volume de mensagens de log detalhadas. Para controlar os custos e não exceder os limites de armazenamento de dados, registre Trace e nivele as mensagens em um armazenamento de dados de alto volume e Debug baixo custo. Considere limitar Trace e Debug a categorias específicas.
    • O registro em log em Warning níveis de passagem Critical deve produzir poucas mensagens de log.
      • Custos e limites de armazenamento geralmente não são uma preocupação.
      • Poucos logs permitem mais flexibilidade nas opções de armazenamento de dados.
  • Em desenvolvimento:
    • Definido como Warning.
    • Adicione Trace ou Debug mensagens durante a solução de problemas. Para limitar a produção, defina Trace ou Debug apenas para as categorias sob investigação.

Os seguintes conjuntos Logging:Console:LogLevel:Microsoft:InformationJSON:

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

ID do evento de log

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

using Microsoft.Extensions.Logging;

internal static class AppLogEvents
{
    internal static EventId Create = new(1000, "Created");
    internal static EventId Read = new(1001, "Read");
    internal static EventId Update = new(1002, "Updated");
    internal static EventId Delete = new(1003, "Deleted");

    // These are also valid EventId instances, as there's
    // an implicit conversion from int to an EventId
    internal const int Details = 3000;
    internal const int Error = 3001;

    internal static EventId ReadNotFound = 4000;
    internal static EventId UpdateNotFound = 4001;

    // ...
}

Gorjeta

Para obter mais informações sobre como converter um int em um EventId, consulte EventId.Implicit(Int32 to EventId) Operator.

Um 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 ID, na mensagem de log, ou não registrar. O provedor de depuração não mostra IDs de evento. 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 os argumentos são fornecidos. Use nomes para os espaços reservados, não números. A ordem dos espaços reservados, não seus nomes, determina quais parâmetros são usados para fornecer seus valores. No código a seguir, os nomes dos parâmetros 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

Nota

Esteja atento ao usar vários espaços reservados em um único modelo de mensagem, pois eles são baseados em ordinais. Os nomes não são usados para alinhar os argumentos aos espaços reservados.

Essa abordagem permite que os provedores de log implementem log semântico ou estruturado. Os argumentos em si são passados para o sistema de registro, não apenas para o modelo de mensagem formatada. Isso permite que os 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 fazer logon no Armazenamento de Tabela do Azure:

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

Formatação do modelo de mensagem de log

Os modelos de mensagens de log suportam formatação de espaço reservado. Os modelos são livres para especificar qualquer formato válido para o argumento de tipo determinado. Por exemplo, considere o seguinte Information modelo de mensagem do registrador:

_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022

No exemplo anterior, a DateTimeOffset instância é o tipo que corresponde ao PlaceHolderName modelo de mensagem do registrador. Esse nome pode ser qualquer coisa, pois os valores são baseados em ordinais. O MMMM dd, yyyy formato é válido para o DateTimeOffset tipo.

Para obter mais informações sobre DateTime formatação e DateTimeOffset formatação, consulte Cadeias de caracteres de formato de data e hora personalizadas.

Exemplos

Os exemplos a seguir mostram como formatar um modelo de mensagem usando a sintaxe de {} espaço reservado. Além disso, um exemplo de escape da sintaxe de espaço reservado {} é mostrado com sua saída. Finalmente, a interpolação de cadeia de caracteres com espaços reservados para modelos também é mostrada:

logger.LogInformation("Number: {Number}", 1);               // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3);           // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5);    // {Number}: 5

Gorjeta

  • Na maioria dos casos, você deve usar a formatação do modelo de mensagem de log ao registrar em log. O uso de interpolação de cadeia de caracteres pode causar problemas de desempenho.
  • Regra de análise de código CA2254: O modelo deve ser uma expressão estática ajuda a alertá-lo para locais onde suas mensagens de log não usam formatação adequada.

Exceções de log

Os métodos do registrador têm sobrecargas que usam um parâmetro de exceção:

public void Test(string id)
{
    try
    {
        if (id is "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ções é 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 trabalhador:

  • Criado com os modelos do .NET Worker.
  • appsettings.json e appsettings. Development.json excluído ou renomeado.

Com a configuração anterior, navegar para a privacidade ou página inicial produz muitos Trace, Debuge Information mensagens com Microsoft o 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:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.SetMinimumLevel(LogLevel.Warning);

using IHost host = builder.Build();

await host.RunAsync();

Função de filtro

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:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddFilter((provider, category, logLevel) =>
{
    return provider.Contains("ConsoleLoggerProvider")
        && (category.Contains("Example") || category.Contains("Microsoft"))
        && logLevel >= LogLevel.Information;
});

using IHost host = builder.Build();

await host.RunAsync();

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 agrupa um conjunto de operações lógicas. Esse agrupamento pode ser usado para anexar os mesmos dados a cada log criado como parte de um conjunto. Por exemplo, cada log criado como parte do processamento de uma transação pode incluir o ID da transação.

Um âmbito:

Os seguintes provedores oferecem suporte a escopos:

Use um escopo encapsulando chamadas de logger em um using bloco:

public async Task<T> GetAsync<T>(string id)
{
    T result;
    var transactionId = Guid.NewGuid().ToString();

    using (_logger.BeginScope(new List<KeyValuePair<string, object>>
        {
            new KeyValuePair<string, object>("TransactionId", transactionId),
        }))
    {
        _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 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 escopos para o provedor de console:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

using IHost host = builder.Build();

await host.RunAsync();

Criar logs no Main

O código a seguir efetua login Main obtendo uma ILogger instância do DI depois de criar o host:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using IHost host = Host.CreateApplicationBuilder(args).Build();

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

await host.RunAsync();

O código anterior depende de dois pacotes NuGet:

Seu arquivo de projeto seria semelhante ao seguinte:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
  </ItemGroup>

</Project>

Nenhum método de logger assíncrono

O registro em log deve ser tão rápido que não vale a pena o custo de desempenho do código assíncrono. Se um armazenamento de dados de registro estiver lento, não grave diretamente nele. Considere gravar as mensagens de log em um armazenamento rápido inicialmente e, em seguida, movê-las para o armazenamento lento mais tarde. Por exemplo, ao registrar em log 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 peça a um operador em segundo plano que retire as mensagens da fila para fazer o trabalho assíncrono de enviar 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 tem 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 está em execução, o aplicativo pode chamar IConfigurationRoot.Reload para atualizar a configuração de log do aplicativo.

Pacotes NuGet

As ILogger<TCategoryName> interfaces e implementações e ILoggerFactory implementações estão incluídas na maioria dos SDKs .NET como referência de pacote implícita. Eles também estão disponíveis explicitamente nos seguintes pacotes NuGet quando não são implicitamente referenciados:

Para obter mais informações sobre qual SDK do .NET inclui referências implícitas de pacote, consulte .NET SDK: tabela para namespace implícito.

Consulte também