Partekatu bidez


Guía de registro de logs para autores de bibliotecas de .NET

Como autor de la biblioteca, la exposición del proceso de registro es una excelente manera de proporcionar a los consumidores información sobre el funcionamiento interno de su biblioteca. Esta guía le ayuda a exponer el registro de una manera coherente con otras bibliotecas y marcos de .NET. También le ayuda a evitar cuellos de botella de rendimiento comunes.

Cuándo usar la ILoggerFactory interfaz

Al escribir una biblioteca que emite registros, necesita un ILogger objeto para registrar los registros. Para obtener ese objeto, la API puede aceptar un ILogger<TCategoryName> parámetro o puede aceptar un ILoggerFactory después del cual se llama a ILoggerFactory.CreateLogger. ¿Qué enfoque se debe preferir?

  • Cuando necesite un objeto de registro que se pueda pasar a varias clases para que todos puedan emitir registros, use ILoggerFactory. Se recomienda que cada clase cree registros con una categoría independiente, denominada igual que la clase . Para ello, necesita que el generador cree objetos únicos ILogger<TCategoryName> para cada clase que emita registros. Entre los ejemplos comunes se incluyen las API de punto de entrada públicas para una biblioteca o constructores públicos de tipos que podrían crear clases auxiliares internamente.
  • Cuando necesite un objeto de registro que solo se use dentro de una clase y nunca se comparta, use ILogger<TCategoryName>, donde TCategoryName es el tipo que genera los registros. Un ejemplo común de esto es un constructor para una clase creada por la inserción de dependencias.

Si va a diseñar una API pública que debe permanecer estable con el tiempo, tenga en cuenta que puede querer refactorizar la implementación interna en el futuro. Incluso si una clase no crea ningún tipo auxiliar interno inicialmente, esto podría cambiar a medida que evoluciona el código. El uso ILoggerFactory de admite la creación de nuevos ILogger<TCategoryName> objetos para cualquier clase nueva sin cambiar la API pública.

Para obtener más información, consulte Cómo se aplican las reglas de filtrado.

Preferir el registro generado por el origen

La ILogger API admite dos enfoques para usar la API. Puede llamar a métodos como LoggerExtensions.LogError y LoggerExtensions.LogInformation, o bien puede utilizar el generador de código fuente para registro para definir métodos de registro fuertemente tipados. Para la mayoría de las situaciones, se recomienda el generador de código fuente porque ofrece un rendimiento superior y tipificación más estricta. También aísla los problemas específicos del registro, como las plantillas de mensaje, los identificadores y los niveles de registro del código de llamada. El enfoque no generado por el código fuente es principalmente útil para escenarios en los que estás dispuesto a renunciar a estas ventajas para que el código sea más 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);
}

El código anterior:

  • Define un tipo partial class llamado LogMessages, que es static para que se pueda utilizar para definir métodos de extensión en el tipo ILogger.
  • Decora un método de extensión LogProductSaleDetails con el atributo LoggerMessage y la plantilla Message.
  • Declara LogProductSaleDetails, que extiende ILogger y acepta un quantity y description.

Sugerencia

Puede acceder al código generado a partir del origen durante la depuración, ya que forma parte del mismo ensamblado que el código que lo invoca.

Uso IsEnabled para evitar una evaluación de parámetros costosa

Puede haber situaciones en las que la evaluación de parámetros sea costosa. Ampliando el ejemplo anterior, imagine que el parámetro description es un string que es costoso de calcular. Quizás el producto que se vende obtiene una descripción de producto amigable y depende de una consulta de base de datos o de la lectura de un archivo. En estas situaciones, puede indicar al generador de código que omita la IsEnabled protección y agregue manualmente la IsEnabled protección en el punto de invocación. Esto permite al usuario determinar dónde se llama a la protección y garantiza que los parámetros que podrían ser costosos de calcular solo se evalúan cuando realmente sea necesario. Observe el código siguiente:

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);
}

Cuando se llama al LogProductSaleDetails método de extensión, se invoca manualmente la IsEnabled protección y la evaluación costosa de parámetros se realiza solo cuando es necesario. Observe el código siguiente:

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

    _logger.LogProductSaleDetails(
        quantity,
        description);
}

Para obtener más información, consulte el registro de orígenes en tiempo de compilación y el registro de alto rendimiento en .NET.

Evitar la interpolación de cadenas en el registro de eventos

Un error común es usar la interpolación de cadenas para compilar mensajes de registro. La interpolación de cadenas en los registros es problemática para el rendimiento, ya que la cadena se evalúa incluso si la correspondiente LogLevel no está habilitada. En lugar de la interpolación de cadenas, use la plantilla de mensaje de registro, el formato y la lista de argumentos. Para obtener más información, consulte Registro en .NET: plantilla de mensaje de registro.

Uso de valores predeterminados de registro no-op

Cuando se consume una biblioteca que expone las API de registro de eventos que esperan o ILogger o ILoggerFactory, puede haber ocasiones en las que no quiera proporcionar un registrador. En estos casos, el paquete NuGet Microsoft.Extensions.Logging.Abstracciones proporciona registros sin operación como valores predeterminados.

Los consumidores de la biblioteca pueden usar como predeterminado un registro nulo si no se proporciona un ILoggerFactory. El uso de logging nulo difiere de definir tipos como anulables (ILoggerFactory?), ya que los tipos no son nulos. Estos tipos orientados a la conveniencia no registran nada y son esencialmente operaciones nulas. Considere la posibilidad de usar cualquiera de los tipos de abstracción disponibles cuando corresponda: