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 logging. Os provedores básicos de logging são integrados, e há também muitos provedores de terceiros disponíveis.
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 ILoggerFactory. O
ILoggerFactory
armazena toda a configuração que determina para onde as mensagens de log são enviadas. Neste caso, configura-se o provedor de logging 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 peloILogger
objeto. Ele é usado para agrupar mensagens de log da mesma classe (ou categoria) ao pesquisar ou filtrar logs. - Chama LogInformation para registar uma mensagem ao nível
Information
. 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 de mensagem"Hello World! Logging is {Description}."
e um par de chave-valorDescription = fun
. 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="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.3" />
</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
eILogger
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 comoLogInformation
. A geração de registos oferece melhor desempenho, tipagem mais robusta e evita a disseminação de constantesstring
nos seus 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 mais simples.
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");
}
}
- Caso não utilizes os logs do console como a tua única solução de monitorização de produção, adiciona os fornecedores de logging que planeias usar. Por exemplo, você pode usar OpenTelemetry para enviar logs por OTLP (protocolo OpenTelemetria):
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 as solicitações da Web de entrada para a execução da funçãoExampleHandler.HandleRequest
. - A linha 12 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 de ILogger e indica qual categoria tem oILogger
objeto. O contêiner DI localiza umILogger
com a categoria correta e o fornece como o argumento do construtor. Se ainda não existir umILogger
com essa categoria, o contêiner DI a criará automaticamente aILoggerFactory
partir do provedor de serviços. - O
logger
parâmetro recebido no construtor foi usado para registar na funçãoHandleRequest
.
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, pode ajustar a configuração de registo por meio de HostApplicationBuilder.Logging, WebApplicationBuilder.Logging ou de APIs semelhantes em outros hosts. Os hosts também aplicam a configuração de registo a partir de fontes de configuração padrão, como appsettings.json e variáveis de 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 - Foi criada uma instância do
ExampleService
do contêiner de DI, que também criou automaticamente umILogger<ExampleService>
para usar como um argumento do construtor. - Invocado
ExampleService.DoSomeWork
que usou oILogger<ExampleService>
para registrar uma mensagem no console.
Configurar registo de eventos
A configuração de registo é configurada em código ou por meio de fontes externas, como ficheiros 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 .
O seguinte appsettings.Development.json é gerado pelos modelos de serviço do Worker do .NET.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
No JSON anterior:
- As categorias de nível de log
"Default"
,"Microsoft"
, e"Microsoft.Hosting.Lifetime"
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 deWarning
e superior. - A categoria
"Microsoft.Hosting.Lifetime"
é mais específica do que a"Microsoft"
, por isso a categoria"Microsoft.Hosting.Lifetime"
regista no nível de log"Information"
e superior. - Um provedor de log específico não é especificado, portanto,
LogLevel
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 um LogLevel
é especificado, o registo é 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 Log levels.
Uma propriedade de provedor pode especificar uma LogLevel
propriedade.
LogLevel
Sob um provedor, especifica os níveis a registar para esse provedor e substitui as configurações de log que não estão associadas a um 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
sobrepõem 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 acima, 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:
- Passado para o fornecedor.
- 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 suportar escopos de log, IncludeScopes
indica se estão habilitados. Para obter mais informações, consulte escopos de registo.
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
substituem configurações emLogging.LogLevel
. Por exemplo, o nível emDebug.LogLevel.Default
substitui o nível emLogLevel.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 integrados 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.
- Expostos 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 através de código
Para configurar o código de login, use a ILoggingBuilder API. Este pode ser acedido a partir de diferentes locais:
- Ao criar o
ILoggerFactory
diretamente, configure em LoggerFactory.Create. - Ao usar DI sem um host, configure em LoggingServiceCollectionExtensions.AddLogging.
- Ao usar um host, configure com HostApplicationBuilder.Logging, WebApplicationBuilder.Logging ou outras APIs específicas do host.
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 objeto ILogger<TCategoryName> é criado, o objeto ILoggerFactory seleciona uma única regra por provedor para aplicar a esse logger. 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 não for encontrada nenhuma correspondência, selecione todas as regras cujo provedor esteja vazio.
- A partir do resultado da etapa anterior, selecione as regras com o prefixo de categoria correspondente mais longo. Se nenhuma correspondência for localizada, selecione todas as regras que não especificam nenhuma 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 log
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 chamar CreateLogger
usando 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 Log | Valor | Método | Descrição |
---|---|---|---|
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 utiliza 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 de evento de 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
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 registo nos níveis
Trace
ouDebug
produz um grande volume de mensagens detalhadas de log. Para controlar os custos e não exceder os limites de armazenamento de dados, registe mensagens de nívelTrace
eDebug
num armazenamento de dados de alto volume e baixo custo. Considere limitarTrace
eDebug
a categorias específicas. - O logging em
Warning
através dos níveisCritical
deve produzir poucos registos.- 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.
- O registo nos níveis
- Em desenvolvimento:
- Definido como
Warning
. - Adicione mensagens
Trace
ouDebug
durante a solução de problemas. Para limitar a produção, definaTrace
ouDebug
apenas para as categorias sob investigação.
- Definido como
Os seguintes conjuntos JSON Logging:Console:LogLevel:Microsoft:Information
:
{
"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 um Id
e propriedades readonly opcionais Name
. O código-fonte de exemplo utiliza a classe AppLogEvents
para definir identificadores de eventos.
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 fornecedor de registo pode registar a ID do evento num campo de ID, na mensagem de registo, ou optar por não o registar. 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, e não os seus nomes, determina quais parâmetros são usados para atribuir os 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 marcadores de posição 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
eRunTime
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 a formatação de marcadores de posição. 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 instância DateTimeOffset
é do tipo que corresponde ao PlaceHolderName
no 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 mais informações sobre a formatação DateTime
e a formatação DateTimeOffset
, 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 do marcador de posição {}
. 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, deve-se usar a formatação do modelo de mensagem de log ao fazer o registo no 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.
Registar exceções
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 registo de exceções é específico do fornecedor.
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ídos ou renomeados.
Com a configuração anterior, navegar para a página de privacidade ou página inicial produz muitas Trace
, Debug
e Information
mensagens 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:
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:
- É um tipo IDisposable que é devolvido pelo método BeginScope.
- Dura até ser descartado.
Os seguintes provedores oferecem suporte a escopos:
Use um escopo encapsulando em um bloco using
as chamadas de logger.
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 âmbitos para o fornecedor da consola.
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 principal
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 registo 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 registar no log no SQL Server, não faça isso diretamente num método Log
, pois os métodos Log
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 Logging não inclui um cenário para alterar os níveis de log enquanto uma aplicação está em execução. No entanto, alguns fornecedores de configuração são capazes de recarregar a configuração, o que tem efeito imediato na configuração de registo. Por exemplo, o Provedor de Configuração de Ficheiro recarrega a configuração de registo 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 interfaces e implementações de ILogger<TCategoryName> e ILoggerFactory 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:
- As interfaces estão em Microsoft.Extensions.Logging.Abstractions.
- As implementações padrão estão em Microsoft.Extensions.Logging.
Para obter mais informações sobre qual SDK do .NET inclui referências implícitas de pacote, consulte .NET SDK: tabela de namespaces implícitos.