Guía de registro para autores de bibliotecas de .NET
Como autor de la biblioteca, exponer el registro es una excelente manera de proporcionar a los consumidores información sobre el funcionamiento interno de la 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 que pueden no ser obvios de otro modo.
Cuándo usar la interfaz ILoggerFactory
Al escribir una biblioteca que emite registros, necesita un objeto ILogger para registrar los registros. Para obtener ese objeto, la API puede aceptar un parámetro ILogger<TCategoryName> 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 objetosILogger<TCategoryName>
únicos 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>
, dondeTCategoryName
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 de ILoggerFactory
admite la creación de nuevos objetos ILogger<TCategoryName>
para cualquier clase nueva sin cambiar la API pública.
Para más información, consulte Cómo se aplican las reglas de filtro.
Preferir el registro generado por el origen
La API ILogger
admite dos enfoques para usar la API. Puede llamar a métodos como LoggerExtensions.LogError y LoggerExtensions.LogInformation, o puede usar el generador de origen de registro para definir métodos de registro fuertemente tipados. Para la mayoría de las situaciones, se recomienda el generador de origen porque ofrece un rendimiento superior y escritura más rápida. 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 origen es principalmente útil para escenarios en los que está dispuesto a renunciar a esas 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
partial class
con nombreLogMessages
, que esstatic
para que se pueda usar para definir métodos de extensión en el tipoILogger
. - Decora un método de extensión
LogProductSaleDetails
con el atributoLoggerMessage
y la plantillaMessage
. - Declara
LogProductSaleDetails
, que extiendeILogger
y aceptaquantity
ydescription
.
Sugerencia
Puede entrar en el código generado por el código fuente durante la depuración, ya que forma parte del mismo ensamblado que el código que lo llama.
Use IsEnabled
para evitar una evaluación de parámetros costosa
Puede haber situaciones en las que la evaluación de parámetros sea costosa. Expandiendo 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 descriptivo y se basa en una consulta de base de datos o en la lectura de un archivo. En estas situaciones, puede indicar al generador de origen que omita la cláusula de restricción IsEnabled
y agregue manualmente la cláusula de restricción IsEnabled
en el sitio de llamada. 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 procesar solo se evalúan cuando realmente es 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 método de extensión LogProductSaleDetails
, se invoca manualmente la protección IsEnabled
y la evaluación de parámetros costosa se limita a 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 Generación de orígenes de registro en tiempo de compilación y Registro de alto rendimiento en .NET.
Evitar la interpolación de cadenas en el registro
Un error común es usar la interpolación de cadenas para compilar mensajes de registro. La interpolación de cadenas en el registro es problemática para el rendimiento, ya que la cadena se evalúa incluso si no está habilitada la correspondiente LogLevel
. 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 los valores predeterminados de registro sin operación
Puede haber ocasiones, cuando se consume una biblioteca que expone las API de registro que esperan ILogger
o ILoggerFactory
, que no desea proporcionar un registrador. En estos casos, el paquete NuGet Microsoft.Extensions.Logging.Abstractions proporciona valores predeterminados de registro sin operación.
Los consumidores de bibliotecas pueden establecer de forma predeterminada un registro null si no se proporciona ninguna ILoggerFactory
. El uso del registro null difiere de definir tipos como que aceptan valores null (ILoggerFactory?
), ya que los tipos no son null. Estos tipos basados en comodidad no registran nada y básicamente no son operaciones. Considere la posibilidad de usar cualquiera de los tipos de abstracción disponibles cuando corresponda: