Materiale sussidiario sulla registrazione per gli autori di librerie .NET
In qualità di autore di una libreria, esporre la registrazione è un ottimo modo per fornire ai consumer informazioni dettagliate sul funzionamento interno della libreria. Questo materiale sussidiario consente di esporre la registrazione in modo coerente con altre librerie e framework .NET. Consente inoltre di evitare colli di bottiglia comuni che potrebbero non essere altrimenti evidenti.
Quando si scrive una libreria che genera i log, è necessario un oggetto ILogger per registrare i log. Per ottenere tale oggetto, l'API può accettare un parametro ILogger<TCategoryName>, oppure può accettare un ILoggerFactory dopo il quale si chiama ILoggerFactory.CreateLogger. Quale approccio preferire?
Quando è necessario un oggetto di registrazione che può essere passato a più classi in modo che tutte possano generare log, usare
ILoggerFactory
. È consigliabile che ogni classe crei log con una categoria separata, che abbia lo stesso nome della classe. A tale scopo, è necessario che la factory crei oggettiILogger<TCategoryName>
univoci per ogni classe che genera log. Esempi comuni includono API del punto di ingresso pubblico per una libreria o costruttori pubblici di tipi che potrebbero creare classi helper internamente.Quando è necessario un oggetto di registrazione usato solo all'interno di una classe e mai condiviso, usare
ILogger<TCategoryName>
, in cuiTCategoryName
è il tipo che produce i log. Un esempio comune di questo tipo è il costruttore di una classe creata tramite iniezione di dipendenza.
Se si progetta un'API pubblica che deve rimanere stabile nel tempo, tenere presente che potrebbe essere opportuno effettuare il refactoring dell'implementazione interna in futuro. Anche se inizialmente una classe non crea alcun tipo di helper interno, ciò potrebbe cambiare man mano che il codice si evolve. L'uso di ILoggerFactory
consente di creare nuovi oggetti ILogger<TCategoryName>
per qualsiasi nuova classe senza modificare l'API pubblica.
Per altre informazioni, vedere Come vengono applicate le regole di filtro.
L'API ILogger
supporta due approcci all'uso dell'API. È possibile chiamare metodi come LoggerExtensions.LogError e LoggerExtensions.LogInformation, oppure usare il generatore di origine della registrazione per definire metodi di registrazione fortemente tipizzati. Per la maggior parte delle situazioni, è consigliabile usare il generatore di origine, in quanto offre prestazioni superiori e una tipizzazione più forte. Inoltre, isola dal codice chiamante i problemi specifici della registrazione, ad esempio modelli di messaggio, ID e livelli di log. L'approccio non generato dall'origine è utile principalmente per gli scenari in cui si è disposti a rinunciare a tali vantaggi per rendere il codice più 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);
}
Il codice precedente:
- Definisce un
partial class
oggetto denominatoLogMessages
, che èstatic
, in modo che possa essere usato per definire i metodi di estensione del tipoILogger
. - Decora un metodo di estensione
LogProductSaleDetails
con l'attributoLoggerMessage
e il modelloMessage
. - Dichiara
LogProductSaleDetails
, che estende ilILogger
e accetta unquantity
edescription
.
Suggerimento
È possibile eseguire l'istruzione nel codice generato dall'origine durante il debug, perché fa parte dello stesso assembly del codice che lo chiama.
Possono verificarsi situazioni in cui la valutazione dei parametri è costosa. Espandendo l'esempio precedente, si supponga che il parametro description
sia un string
costoso da calcolare. Ad esempio, il prodotto venduto ottiene una descrizione amichevole del prodotto e si basa su una query del database o sulla lettura di un file. In queste situazioni, è possibile indicare al generatore di origine di ignorare la guard IsEnabled
e aggiungere manualmente la guard IsEnabled
nel sito di chiamata. Ciò consente all'utente di determinare dove viene chiamata la guard e garantisce che i parametri che potrebbero essere costosi da calcolare vengano valutati solo quando veramente necessari. Osservare il codice seguente:
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 viene chiamato il metodo di estensione LogProductSaleDetails
, la guard IsEnabled
viene richiamata manualmente e la costosa valutazione dei parametri viene limitata al momento in cui è necessaria. Osservare il codice seguente:
if (_logger.IsEnabled(LogLevel.Information))
{
// Expensive parameter evaluation
var description = product.GetFriendlyProductDescription();
_logger.LogProductSaleDetails(
quantity,
description);
}
Per altre informazioni, vedere Generazione dell'origine di registrazione in fase di compilazione e Registrazione ad alte prestazioni in .NET.
Un errore comune consiste nell'usare l'interpolazione di stringhe per creare messaggi di log. L'interpolazione di stringhe durante la registrazione è problematica in termini di prestazioni, poiché la stringa viene valutata anche se la corrispondente LogLevel
non è abilitata. Anziché l'interpolazione di stringhe, usare il modello di messaggio di log, la formattazione e l'elenco di argomenti. Per altre informazioni, vedere Registrazione in .NET: modello di messaggio di log.
In alcuni casi, quando si usa una libreria che espone le API di registrazione che prevedono un ILogger
oggetto o ILoggerFactory
, che non si vuole fornire un logger. In questi casi, il pacchetto NuGet Microsoft.Extensions.Logging.Abstractions fornisce impostazioni predefinite di registrazione senza operazioni.
I consumer di libreria possono impostare come impostazione predefinita la registrazione Null se non viene fornito alcun ILoggerFactory
valore. L'uso della registrazione Null differisce dalla definizione dei tipi come nullable (ILoggerFactory?
), in quanto i tipi non sono Null. Questi tipi basati su praticità non registrano nulla e sono essenzialmente no-op. È consigliabile usare uno dei tipi astratti disponibili, se applicabile:
Feedback su .NET
.NET è un progetto di open source. Selezionare un collegamento per fornire feedback: