Partilhar via


Diretrizes de log para autores de bibliotecas .NET

Como autor de uma biblioteca, disponibilizar logs é uma ótima maneira de fornecer aos consumidores informações sobre o funcionamento interno da sua biblioteca. Esta orientação ajuda você a expor o registro em log de forma consistente com outras bibliotecas e estruturas do .NET. Também ajuda a evitar gargalos de desempenho comuns que podem não ser óbvios de outra forma.

Quando usar a ILoggerFactory interface

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

  • Quando precisar de um objeto de registo de logs que possa ser partilhado com várias classes para que todas elas possam gerar logs, use ILoggerFactory. É recomendável que cada classe crie logs com uma categoria separada, com o mesmo nome da classe. Para fazer isso, você precisa que a fábrica crie objetos exclusivos ILogger<TCategoryName> 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 você precisar de um objeto de log que seja usado apenas dentro de uma classe e nunca compartilhado, use ILogger<TCategoryName>, onde TCategoryName é o tipo que produz os logs. Um exemplo comum disso é um construtor para uma classe criada por injeção de dependência.

Se você 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 mudar à medida que o código evolui. O uso ILoggerFactory acomoda a criação de novos ILogger<TCategoryName> objetos para quaisquer novas classes sem alterar a API pública.

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

Prefira o registro em log gerado pelo código-fonte

A ILogger API suporta duas abordagens para usar a API. Você pode chamar métodos como LoggerExtensions.LogError e LoggerExtensions.LogInformation, ou pode usar o gerador de fonte de registo para definir métodos de registo fortemente tipados. Para a maioria das situações, o gerador de origem é recomendado porque oferece desempenho superior e digitação mais forte. Ele também isola questões específicas relacionadas ao registo, como modelos de mensagem, IDs e níveis de registo do código chamador. A abordagem não gerada pelo código-fonte é útil principalmente para cenários em que 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 denominado LogMessages, que é static para ser utilizado para definir métodos de extensão no tipo ILogger.
  • Decora um LogProductSaleDetails método de extensão com o LoggerMessage atributo e o Message modelo.
  • Declara LogProductSaleDetails, que estende o ILogger e aceita a quantity e description.

Sugestão

Você pode entrar no código gerado a partir do fonte durante a depuração, porque ele faz parte da mesma assemblagem que o código que o chama.

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

Pode haver situações em que a avaliação de parâmetros é cara. Expandindo o exemplo anterior, imagine que o description parâmetro é um string que é caro de calcular. Talvez o produto que está sendo vendido obtenha uma descrição amigável do produto e dependa de uma consulta de banco de dados ou leitura de um arquivo. Nessas situações, pode instruir o gerador de código-fonte a ignorar o IsEnabled protetor e adicionar manualmente o IsEnabled protetor no ponto de chamada. Isso permite que o usuário determine onde o protetor é chamado e garante que os parâmetros que podem ser caros para calcular sejam avaliados apenas quando realmente necessário. Considere o seguinte código:

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 LogProductSaleDetails extensão é chamado, o protetor IsEnabled é invocado manualmente e a avaliação de parâmetros com custo elevado é realizada apenas quando necessário. Considere o seguinte código:

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

    _logger.LogProductSaleDetails(
        quantity,
        description);
}

Para obter mais informações, consulte Geração de origem de log em tempo de compilação e Log de alto desempenho no .NET.

Evite a interpolação de cadeias de caracteres no log

Um erro comum é usar a interpolação de cadeia de caracteres para criar mensagens de log. A interpolação de cadeia de caracteres no log é problemática para o desempenho, pois a cadeia de caracteres é avaliada mesmo que a correspondente LogLevel não esteja habilitada. Em vez de interpolação de cadeia de caracteres, use o modelo de mensagem de log, formatação e lista de argumentos. Para obter mais informações, consulte Fazendo login no .NET: modelo de mensagem de log.

Usar definições padrão de registo no-op

Pode haver momentos, ao consumir uma biblioteca que expõe APIs de log que esperam um ILogger ou ILoggerFactory, que você não deseja fornecer um registrador. Nesses casos, o pacote Microsoft.Extensions.Logging.Abstractions NuGet fornece valores padrão para no-op de registo.

Os utilizadores de biblioteca podem usar por defeito o log nulo caso nenhum ILoggerFactory seja fornecido. O uso de registro nulo difere da definição de tipos como anuláveis (ILoggerFactory?), uma vez que os tipos não são nulos. Esses tipos por conveniência não registam nada e são essencialmente operações nulas. Considere a utilização de qualquer um dos tipos de abstração disponíveis, quando aplicável: