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 peloILogger
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 parDescription = fun
chave-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
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 origem de registro em log oferece melhor desempenho, digitação mais forte e evita espalharstring
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");
}
}
- Se você não usar os logs do console como sua única solução de monitoramento de produção, adicione os provedores de log que você planeja 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 solicitações da Web de entrada para executar aExampleHandler.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 oILogger
objeto tem. O contêiner DI localiza umILogger
com a categoria correta e o fornece como o argumento do construtor. Se ainda nãoILogger
existir essa categoria, o contêiner DI a criará automaticamente aILoggerFactory
partir do no provedor de serviços. - O
logger
parâmetro recebido no construtor foi usado para registrar naHandleRequest
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 umILogger<ExampleService>
para usar como o argumento do construtor. - Invocado
ExampleService.DoSomeWork
que usou oILogger<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 deWarning
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, 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 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 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 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:
- 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.Loggingou WebApplicationBuilder.Logging 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 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 ouDebug
produz um grande volume de mensagens de log detalhadas. Para controlar os custos e não exceder os limites de armazenamento de dados, registreTrace
e nivele as mensagens em um armazenamento de dados de alto volume eDebug
baixo custo. Considere limitarTrace
eDebug
a categorias específicas. - O registro em log em
Warning
níveis de passagemCritical
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.
- O registro em log nos
- Em desenvolvimento:
- Definido como
Warning
. - Adicione
Trace
ouDebug
mensagens 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 Logging:Console:LogLevel:Microsoft:Information
JSON:
{
"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
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 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
, Debug
e 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:
- É um IDisposable tipo que é retornado pelo BeginScope método.
- Dura até ser descartado.
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:
- 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 para namespace implícito.