Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Pour les scénarios de journalisation hautes performances dans .NET 6 et versions ultérieures, utilisez la LoggerMessageAttributegénération de source au moment de la compilation. Cette approche offre les meilleures performances en éliminant le boxing, les allocations temporaires et l'analyse des modèles de messages lors de l'exécution.
La journalisation générée par la source offre les avantages de performances suivants par rapport aux méthodes d’extension d’enregistreur d’événements, telles que LogInformation :LogDebug
-
Élimine l'encapsulation : Les méthodes d’extension du logger nécessitent l'encapsulation (conversion) des types valeur, tels que
int, enobject. La journalisation générée par la source évite le boxing en utilisant des paramètres fortement typés. - Analyse les modèles au moment de la compilation : Les méthodes d’extension d’enregistreur d’événements doivent analyser le modèle de message (chaîne de format nommée) chaque fois qu’un message de journal est écrit. La journalisation générée par la source analyse les modèles une fois au moment de la compilation.
- Réduit les allocations : Le générateur source crée du code optimisé qui réduit les allocations d’objets et l’utilisation temporaire de la mémoire.
L'exemple d'application illustre les fonctionnalités de journalisation haute performance avec un service de travailleur traitant les files d'attente prioritaires. L’application traite les éléments de travail dans l’ordre de priorité. À mesure que ces opérations se produisent, les messages de journalisation sont générés à l’aide de la journalisation générée par la source.
Conseil / Astuce
Tous les exemples de code source de journalisation sont disponibles dans l’Explorateur d’exemples pour téléchargement. Pour plus d’informations, consultez Parcourir les exemples de code : Journalisation dans .NET.
Définir des messages de journal avec la génération de code source
Pour créer des messages de journal hautes performances dans .NET 6 et versions ultérieures, définissez partial des méthodes décorées avec LoggerMessageAttribute. Le générateur source crée l’implémentation au moment de la compilation.
Méthode de journalisation de base
Pour un message de journal simple, définissez une méthode partielle avec l’attribut spécifiant l’ID d’événement, le niveau de journal et le modèle de message :
public static partial class Log
{
[LoggerMessage(
EventId = 13,
Level = LogLevel.Critical,
Message = "Epic failure processing item!")]
public static partial void FailedToProcessWorkItem(
ILogger logger, Exception ex);
}
Le modèle de message utilise des espaces réservés remplis par les paramètres de méthode. Les noms d’espaces réservés doivent être descriptifs et cohérents entre les modèles. Ils servent de noms de propriétés dans les données de journal structurées. Nous recommandons d'utiliser la casse Pascal pour les noms d’espaces réservés. Par exemple, {Item}, {DateTime}.
Appelez la méthode de journalisation à partir de votre code. Par exemple, lorsqu’une exception se produit pendant le traitement des éléments de travail :
try
{
// Process work item.
}
catch (Exception ex)
{
Log.FailedToProcessWorkItem(logger, ex);
}
Ce code produit une sortie de console comme :
crit: WorkerServiceOptions.Example.Worker[13]
Epic failure processing item!
System.Exception: Failed to verify communications.
Journalisation avec des paramètres
Pour passer des paramètres à un message de journal, ajoutez-les en tant que paramètres de méthode. Les noms de paramètres correspondent aux espaces réservés dans le modèle de message :
public static partial class Log
{
[LoggerMessage(
EventId = 1,
Level = LogLevel.Information,
Message = "Processing priority item: {Item}")]
public static partial void PriorityItemProcessed(
ILogger logger, WorkItem item);
}
Appelez la méthode avec les valeurs d’enregistreur d’événements et de paramètres :
var workItem = queue.Dequeue();
Log.PriorityItemProcessed(logger, workItem);
Ce code produit une sortie de console comme :
info: WorkerServiceOptions.Example.Worker[1]
Processing priority item: Priority-Extreme (50db062a-9732-4418-936d-110549ad79e4): 'Verify communications'
Les répertoires de journalisation structurée peuvent utiliser le nom de l’événement lorsqu’il est fourni avec l’ID de l’événement afin d’enrichir la journalisation. Par exemple, Serilog utilise le nom de l’événement.
Définir l'étendue du message du logger avec la génération de code source
Vous pouvez définir des étendues de journal pour encapsuler une série de messages de journal avec un contexte supplémentaire. Avec la journalisation générée par la source, vous combinez les LoggerMessageAttribute méthodes avec la méthode standard ILogger.BeginScope .
Activez IncludeScopes dans la section enregistreur d’événements de console de appsettings.json:
{
"Logging": {
"Console": {
"IncludeScopes": true
},
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Créez des méthodes de journalisation générées à partir de la source et intégrez-les dans un contexte à l’aide de BeginScope.
public static partial class Log
{
[LoggerMessage(
EventId = 1,
Level = LogLevel.Information,
Message = "Processing priority item: {Item}")]
public static partial void PriorityItemProcessed(
ILogger logger, WorkItem item);
}
Utilisez la méthode de journalisation dans le contexte du code de votre application :
using (_logger.BeginScope("Processing scope, started at: {DateTime}", DateTime.Now))
{
Log.PriorityItemProcessed(_logger, workItem);
}
Inspectez les messages de log dans la console de l'application. Le résultat suivant montre le classement par priorité des messages de journal avec le message d’étendue du journal inclus :
info: WorkerServiceOptions.Example.Worker[1]
=> Processing scope, started at: 04/11/2024 11:27:52
Processing priority item: Priority-Extreme (7d153ef9-8894-4282-836a-8e5e38319fb3): 'Verify communications'
Approche héritée : LoggerMessage.Define (pour .NET Framework et .NET Core 3.1)
Avant que la journalisation générée par la source ait été introduite dans .NET 6, l’approche recommandée de journalisation hautes performances était d’utiliser la LoggerMessage.Define méthode pour créer des délégués pouvant être mis en cache. Bien que cette approche soit toujours prise en charge pour la compatibilité descendante, le nouveau code doit utiliser la journalisation générée par la source avec LoggerMessageAttribute à la place.
La LoggerMessage classe expose des fonctionnalités pour créer des délégués pouvant être mis en cache qui nécessitent moins d’allocations d’objets et une surcharge de calcul réduite par rapport aux méthodes d’extension d’enregistreur d’événements, telles que LogInformation et LogDebug. LoggerMessage offre les avantages de performances suivants par rapport aux méthodes d’extension d’enregistreur d’événements :
- Les méthodes d’extension de journaliseur nécessitent la conversion (« boxing ») de types de valeur, tels que
int, enobject. En utilisant des champs LoggerMessage statiques et des méthodes d’extension avec des paramètres fortement typés, le modèle Action évite le boxing. - Les méthodes d’extension de journaliseur doivent analyser le modèle de message (chaîne de format nommé) chaque fois qu’un message de journal est écrit. LoggerMessage requiert l’analyse d’un modèle une seule fois quand le message est défini.
Note
Si vous conservez du code qui utilise LoggerMessage.Define, envisagez de migrer vers la journalisation générée par la source. Pour les applications .NET Framework ou .NET Core 3.1, continuez à utiliser LoggerMessage.Define.
Définir un message d’enregistreur d’événements
Utilisez Define(LogLevel, EventId, String) pour créer un Action délégué pour la journalisation d’un message. Define les surcharges permettent de transmettre jusqu’à six paramètres de type à une chaîne de format nommée (modèle).
La chaîne fournie à la Define méthode est un modèle et non une chaîne interpolée. Les espaces réservés sont renseignés dans l’ordre dans lequel les types sont spécifiés. Les noms d’espaces réservés dans le modèle doivent être descriptifs et cohérents entre les modèles. Ils servent de noms de propriétés dans les données de journal structurées. Nous recommandons d'utiliser la casse Pascal pour les noms d’espaces réservés. Par exemple, {Item}, {DateTime}.
Chaque message de journal est conservé Action dans un champ statique créé par LoggerMessage.Define. Par exemple, l’exemple d’application crée un champ pour décrire un message de journal pour le traitement des éléments de travail :
private static readonly Action<ILogger, Exception> s_failedToProcessWorkItem;
Pour le Action, spécifiez :
- Niveau du journal.
- Identificateur d’événement unique (EventId) avec le nom de la méthode d’extension statique.
- Modèle de message (chaîne de format nommée).
À mesure que les éléments de travail sont retirés de la file d'attente pour le traitement, l'application de service de travailleur définit les éléments suivants :
- Niveau de journal à LogLevel.Critical.
- ID d’événement à
13, avec le nom de la méthodeFailedToProcessWorkItem. - Convertir le modèle de message (chaîne de format nommée) en chaîne.
s_failedToProcessWorkItem = LoggerMessage.Define(
LogLevel.Critical,
new EventId(13, nameof(FailedToProcessWorkItem)),
"Epic failure processing item!");
La LoggerMessage.Define méthode est utilisée pour configurer et définir un Action délégué, qui représente un message de journal.
Les répertoires de journalisation structurée peuvent utiliser le nom de l’événement lorsqu’il est fourni avec l’ID de l’événement afin d’enrichir la journalisation. Par exemple, Serilog utilise le nom de l’événement.
Action est invoqué à travers une méthode d’extension fortement typée. La PriorityItemProcessed méthode enregistre un message chaque fois qu’un élément de travail est traité.
FailedToProcessWorkItem est appelé si et quand une exception se produit :
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
using (IDisposable? scope = logger.ProcessingWorkScope(DateTime.Now))
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
WorkItem? nextItem = priorityQueue.ProcessNextHighestPriority();
if (nextItem is not null)
{
logger.PriorityItemProcessed(nextItem);
}
}
catch (Exception ex)
{
logger.FailedToProcessWorkItem(ex);
}
await Task.Delay(1_000, stoppingToken);
}
}
}
Inspectez la sortie de la console de l’application :
crit: WorkerServiceOptions.Example.Worker[13]
Epic failure processing item!
System.Exception: Failed to verify communications.
at WorkerServiceOptions.Example.Worker.ExecuteAsync(CancellationToken stoppingToken) in
..\Worker.cs:line 27
Pour passer des paramètres à un message de journal, définissez jusqu’à six types lors de la création du champ statique. L’exemple d’application enregistre les détails de l’élément de travail lors du traitement des éléments en définissant un WorkItem type pour le Action champ :
private static readonly Action<ILogger, WorkItem, Exception> s_processingPriorityItem;
Le modèle de message de log du délégué reçoit ses valeurs de substitution à partir des types fournis. L’exemple d’application définit un délégué pour l’ajout d’un élément de travail où le paramètre d’élément est un WorkItem:
s_processingPriorityItem = LoggerMessage.Define<WorkItem>(
LogLevel.Information,
new EventId(1, nameof(PriorityItemProcessed)),
"Processing priority item: {Item}");
La méthode d’extension statique pour la journalisation d’un élément de travail en cours de traitement, PriorityItemProcessedreçoit la valeur de l’argument d’élément de travail et la transmet au Action délégué :
public static void PriorityItemProcessed(
this ILogger logger, WorkItem workItem) =>
s_processingPriorityItem(logger, workItem, default!);
Dans la méthode du ExecuteAsync service de travail, PriorityItemProcessed est appelée pour journaliser le message :
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
using (IDisposable? scope = logger.ProcessingWorkScope(DateTime.Now))
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
WorkItem? nextItem = priorityQueue.ProcessNextHighestPriority();
if (nextItem is not null)
{
logger.PriorityItemProcessed(nextItem);
}
}
catch (Exception ex)
{
logger.FailedToProcessWorkItem(ex);
}
await Task.Delay(1_000, stoppingToken);
}
}
}
Inspectez la sortie de la console de l’application :
info: WorkerServiceOptions.Example.Worker[1]
Processing priority item: Priority-Extreme (50db062a-9732-4418-936d-110549ad79e4): 'Verify communications'
Optimisations protégées par niveau de journalisation
Vous pouvez optimiser les performances en vérifiant le LogLevel avec ILogger.IsEnabled(LogLevel) avant d’appeler la méthode Log* correspondante. Lorsque la journalisation n’est pas configurée pour l’élément donné LogLevel, ILogger.Log elle n’est pas appelée. En outre, l'encapsulation de type valeur et une allocation de object[] (pour représenter les paramètres) sont évitées.
Pour plus d’informations, consultez :
- Micro benchmarks dans le runtime .NET
- Contexte et motivation pour les vérifications des niveaux de journalisation