Compartilhar via


Diretrizes de registro em log para autores de bibliotecas .NET

Como autor de biblioteca, expor o registro em log é uma ótima maneira de fornecer aos consumidores informações sobre o funcionamento interno de sua biblioteca. Essas diretrizes ajudam você a expor o log de uma maneira consistente com outras bibliotecas e estruturas do .NET. Ele também ajuda você a evitar gargalos de desempenho comuns.

Quando usar a ILoggerFactory interface

Ao escrever uma biblioteca que emite logs, você precisa de um ILogger objeto para gravar 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. Qual abordagem deve ser preferida?

  • Quando você precisar de um objeto de log que possa ser passado para várias classes para que todas elas possam emitir logs, use ILoggerFactory. É recomendável que cada classe crie logs com uma categoria separada, nomeada da mesma forma que a 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 está 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 criando uma API pública que deve permanecer estável ao longo do tempo, tenha em mente 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 novas classes sem alterar a API pública.

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

Preferir registro em log gerado pela origem

A ILogger API dá suporte a duas abordagens para usar a API. Você pode chamar métodos como LoggerExtensions.LogError e LoggerExtensions.LogInformation, ou pode usar o gerador de origem de log para definir métodos de registro em log 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 preocupações específicas do registro em log, como modelos de mensagem, IDs e níveis de log do código de chamada. A abordagem não gerada pela 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 nomeado LogMessages, que é static para que ele possa ser usado para definir métodos de extensão no ILogger tipo.
  • Decora um método de extensão LogProductSaleDetails com o atributo LoggerMessage e o modelo Message.
  • Declara LogProductSaleDetails, que estende o ILogger e aceita um quantity e description.

Dica

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

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

Pode haver situações em que a avaliação de parâmetros é cara. Expandindo sobre o exemplo anterior, imagine que o parâmetro description seja um string que é caro para computar. 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, você pode instruir o gerador de código-fonte a ignorar IsEnabled guard e adicionar manualmente IsEnabled guard no ponto de chamada. Isso permite que o usuário determine onde o guarda é chamado e garante que os parâmetros que podem ser caros para a computação 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 LogProductSaleDetails método de extensão é chamado, o IsEnabled guard é invocado manualmente, limitando a avaliação dispendiosa dos parâmetros somente 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.

Evitar interpolação de cadeia de caracteres no registro em 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 se o correspondente LogLevel não estiver habilitado. Em vez da interpolação de cadeia de caracteres, use o modelo de mensagem de log, a formatação e a lista de argumentos. Para obter mais informações, consulte Log no .NET: modelo de mensagem de log.

Usar padrões de registro em log no-op

Quando você consome uma biblioteca que expõe APIs de log que esperam um ILogger ou ILoggerFactory, pode haver momentos em que você não deseja fornecer um registrador. Nesses casos, o pacote NuGet Microsoft.Extensions.Logging.Abstractions fornece padrões de log sem operação.

Usuários da biblioteca podem padronizar para o registro em log nulo se não for fornecido um ILoggerFactory. O uso do log nulo difere da definição de tipos como anuláveis (ILoggerFactory?), pois os tipos não são nulos. Esses tipos baseados em conveniência não registram nada e, essencialmente, não realizam qualquer operação. Considere usar qualquer um dos tipos de abstração disponíveis quando aplicável: