Partilhar via


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 pelo ILogger 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 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 registos oferece melhor desempenho, tipagem mais robusta e evita a disseminação de constantes string 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ção ExampleHandler.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 o ILogger objeto. O contêiner DI localiza um ILogger com a categoria correta e o fornece como o argumento do construtor. Se ainda não existir um ILogger com essa categoria, o contêiner DI a criará automaticamente a ILoggerFactory partir do provedor de serviços.
  • O logger parâmetro recebido no construtor foi usado para registar na função HandleRequest.

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 um ILogger<ExampleService> para usar como um argumento do construtor.
  • Invocado ExampleService.DoSomeWork que usou o ILogger<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 de Warning 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 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 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:

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 parâmetro, , é a ID de evento de log. O segundo parâmetro é um modelo de mensagem com espaços reservados para valores de argumento fornecidos pelos parâmetros de método restantes. Os parâmetros 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 registo nos níveis Trace ou Debug 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ível Trace e Debug num armazenamento de dados de alto volume e baixo custo. Considere limitar Trace e Debug a categorias específicas.
    • O logging em Warning através dos níveis Critical 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.
  • Em desenvolvimento:
    • Definido como Warning.
    • Adicione mensagens Trace ou Debug 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 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 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 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:

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:

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.

Consulte também