Compartilhar via


Diretrizes do registro em log para autores de bibliotecas .NET

Como autor de biblioteca, a exposição do registro em log é uma ótima maneira de fornecer aos consumidores uma visão do funcionamento interno da sua biblioteca. Estas diretrizes ajudam você a expor o registro em log de forma consistente com outras bibliotecas e estruturas .NET. Ela também ajuda você a evitar gargalos comuns de desempenho que podem não ser óbvios.

Quando utilizar a interface ILoggerFactory

Ao escrever uma biblioteca que emite logs, você precisa de um objeto ILogger para registrar os logs. Para obter esse objeto, sua API pode aceitar um parâmetro ILogger<TCategoryName> ou pode aceitar um ILoggerFactory após o qual você chama ILoggerFactory.CreateLogger. Qual abordagem deve ser a preferida?

  • Quando precisar de um objeto de registro em log que possa ser transmitido a várias classes para que todas elas possam emitir logs, utilize ILoggerFactory. Recomenda-se que cada classe crie registros em logs com uma categoria separada, com o mesmo nome da classe. Para fazer isso, é necessário que a fábrica crie objetos ILogger<TCategoryName> exclusivos para cada classe que emite logs. Exemplos comuns incluem APIs de ponto de entrada público para uma biblioteca ou construtores públicos de tipos que podem criar classes auxiliares internamente.

  • Quando precisar de um objeto de registro em log que seja usado somente dentro de uma classe e nunca compartilhado, utilize ILogger<TCategoryName>, na qual TCategoryName é o tipo que produz os logs. Um exemplo comum disso é um construtor para uma classe criada por injeção de dependência.

Se estiver projetando uma API pública que deve permanecer estável ao longo do tempo, lembre-se de que talvez deseje refatorar sua implementação interna no futuro. Mesmo que uma classe não crie nenhum tipo auxiliar interno inicialmente, isso pode ser alterado à medida que o código evolui. Utilizar ILoggerFactory acomoda a criação de novos objetos ILogger<TCategoryName> para todas as novas classes sem alterar a API pública.

Para obter mais informações, confira Como as regras de filtragem são aplicadas.

Prefira o registro em log gerado pela origem

A ILogger suporta duas abordagens para o uso da API. Você pode chamar métodos como LoggerExtensions.LogError e LoggerExtensions.LogInformation, ou pode utilizar o gerador de origens de registro em log para definir métodos de registro fortemente tipados. Na maioria das situações, o gerador de origem é recomendado porque oferece desempenho superior e digitação mais forte. Ele também isola preocupações específicas de registro em log, como modelos de mensagens, IDs e níveis de log do código de chamada. A abordagem não gerada pela origem é útil principalmente em cenários nos quais você está disposto a abrir mão dessas vantagens para tornar o código mais conciso.

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages
{
    [LoggerMessage(
        Message = "Sold {Quantity} of {Description}",
        Level = LogLevel.Information)]
    internal static partial void LogProductSaleDetails(
        this ILogger logger,
        int quantity,
        string description);
}

O código anterior:

  • Define um partial class chamado LogMessages, que é static, de modo que possa ser utilizado para definir métodos de extensão no tipo ILogger.
  • Decora um método de extensão LogProductSaleDetails com o atributo LoggerMessage e o modelo Message.
  • Declara LogProductSaleDetails, que estende ILogger e aceita quantity e description.

Dica

Você pode entrar no código gerado pela origem durante a depuração, pois ele faz parte do mesmo assembly que o código que o chama.

Utilize IsEnabled para evitar a avaliação dispendiosa de parâmetros

Podem existir situações nas quais a avaliação de parâmetros é dispendiosa. Expandindo o exemplo anterior, imagine que o parâmetro description é um string cuja computação é dispendiosa. Talvez o produto que está sendo vendido tenha uma descrição amigável e dependa de uma consulta ao banco de dados ou da leitura de um arquivo. Nessas situações, você pode instruir o gerador de origens a ignorar a proteção IsEnabled e adicionar manualmente a proteção IsEnabled no local da chamada. Isso permite que o usuário determine o local em que a proteção é chamada e garante que os parâmetros que podem ser dispendiosos para computação sejam avaliados somente quando realmente necessários. Considere o código a seguir:

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages
{
    [LoggerMessage(
        Message = "Sold {Quantity} of {Description}",
        Level = LogLevel.Information,
        SkipEnabledCheck = true)]
    internal static partial void LogProductSaleDetails(
        this ILogger logger,
        int quantity,
        string description);
}

Quando o método de extensão LogProductSaleDetails é chamado, a proteção IsEnabled é invocada manualmente e a avaliação do parâmetro dispendioso é limitada ao momento em que é necessário. Considere o código a seguir:

if (_logger.IsEnabled(LogLevel.Information))
{
    // Expensive parameter evaluation
    var description = product.GetFriendlyProductDescription();

    _logger.LogProductSaleDetails(
        quantity,
        description);
}

Para obter mais informações, confira Geração de origens de registro em log em tempo de compilação e Registro em log de alto desempenho no .NET.

Evite a interpolação de cadeia de caracteres no registro em log

Um erro comum é utilizar interpolação de cadeia de caracteres para criar mensagens de log. A interpolação da cadeia de caracteres no registro em log é problemática para o desempenho, pois a cadeia é avaliada mesmo que o LogLevel correspondente não esteja habilitado. Em vez da interpolação da cadeia de caracteres, utilize o modelo de mensagens de log, a formatação e a lista de argumentos. Para obter mais informações, confira o Registro em log no .NET: Modelo de mensagens de log.

Utilize os padrões de registro em log sem operações

Pode haver ocasiões em que você pode consumir uma biblioteca que expõe APIs de registro em log que esperam um ILogger ou ILoggerFactory, para as quais você não deseja fornecer um agente. Nesses casos, o pacote NuGet Microsoft.Extensions.Logging.Abstractions fornece padrões de log sem operação.

Os consumidores de biblioteca podem usar o padrão de registro em log nulo se nenhum ILoggerFactory for fornecido. O uso de registro em log nulo difere da definição de tipos como anuláveis (ILoggerFactory?), pois os tipos são não nulos. Esses tipos baseados em conveniência não registram nada em log e são essencialmente sem operações. Considere o uso de qualquer um dos tipos de abstração disponíveis no local em que for aplicável: