Journalisation en C# et .NET
.NET prend en charge la journalisation haute performance structurée via l’API ILogger pour faciliter la supervision du comportement des applications et le diagnostic des problèmes. Les journaux peuvent être écrits dans différentes destinations en configurant différents fournisseurs de journalisation. Les fournisseurs de journalisation de base sont intégrés et de nombreux fournisseurs tiers sont également disponibles.
Bien démarrer
Ce premier exemple montre les principes de base, mais il convient seulement à une application console triviale. Cet exemple d’application console s’appuie sur les packages NuGet suivants :
Dans la section suivante, vous voyez comment améliorer le code en prenant en compte la mise à l’échelle, les performances, la configuration et les modèles de programmation classiques.
using Microsoft.Extensions.Logging;
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
L’exemple précédent :
- Crée un objet ILoggerFactory.
ILoggerFactory
stocke toute la configuration qui détermine l’emplacement où les messages de journal sont envoyés. Dans le cas présent, vous configurez le fournisseur de journalisation de la console afin que les messages de journal soient écrits dans la console. - Crée un ILogger avec une catégorie nommée « Program ». La catégorie est une
string
associée à chaque message consigné par leILogger
. Elle est utilisée pour regrouper les messages de journalisation ayant la même classe (ou catégorie) lors de la recherche ou du filtrage des journaux. - Appelle LogInformation pour journaliser un message au niveau
Information
. Le niveau de journal indique la gravité de l’événement journalisé et est utilisé pour filtrer les messages de journal moins importants. L’entrée de journal inclut également un modèle de message"Hello World! Logging is {Description}."
et une paire clé-valeurDescription = fun
. Le nom de clé (ou l’espace réservé) provient du mot à l’intérieur des accolades dans le modèle et la valeur provient de l’argument de méthode restant.
Ce fichier projet pour cet exemple inclut deux packages NuGet :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
</ItemGroup>
</Project>
Conseil
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.
Journalisation dans une application non triviale
Plusieurs modifications de l’exemple précédent sont à envisager pour la journalisation dans un scénario moins trivial :
Si votre application utilise l’injection de dépendances (DI) ou un hôte comme WebApplication d’ASP.NET ou un hôte générique, vous devez utiliser des objets
ILoggerFactory
etILogger
depuis leurs conteneurs d’injection de dépendances respectifs au lieu de les créer directement. Pour plus d’informations, consultez Intégration à une injection de dépendances et à des hôtes.La journalisation de la génération de source au moment de la compilation est généralement une meilleure alternative aux méthodes d’extension
ILogger
commeLogInformation
. La journalisation de la génération de source offre de meilleures performances et un typage plus fort, et évite de propager des constantesstring
dans toutes vos méthodes. L’inconvénient est que l’utilisation de cette technique nécessite un peu plus de code.
using Microsoft.Extensions.Logging;
internal partial class Program
{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
LogStartupMessage(logger, "fun");
}
[LoggerMessage(Level = LogLevel.Information, Message = "Hello World! Logging is {Description}.")]
static partial void LogStartupMessage(ILogger logger, string description);
}
- La pratique recommandée pour les noms de catégorie de journal est d’utiliser le nom complet de la classe qui crée le message de journal. Ceci permet de relier les messages de journalisation au code qui les a produits et offre un bon niveau de contrôle lors du filtrage des journaux. CreateLogger accepte un
Type
pour faciliter ce nommage.
using Microsoft.Extensions.Logging;
internal class Program
{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger<Program>();
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
}
}
- Si vous n’utilisez pas les journaux de console comme seule solution de supervision de la production, ajoutez les fournisseurs de journalisation que vous prévoyez d’utiliser. Par exemple, vous pouvez utiliser OpenTelemetry pour envoyer des journaux via OTLP (protocole OpenTelemetry) :
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.AddOtlpExporter();
});
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
Intégration avec des hôtes et une injection de dépendances
Si votre application utilise l’injection de dépendances (DI) ou un hôte comme WebApplication d’ASP.NET ou un hôte générique, vous devez utiliser des objets ILoggerFactory
et ILogger
depuis le conteneur d’injection de dépendances au lieu de les créer directement.
Obtenir un ILogger depuis l’injection de dépendances
Cet exemple obtient un objet ILogger dans une application hébergée en utilisant les API minimales d’ASP.NET :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
var handler = app.Services.GetRequiredService<ExampleHandler>();
app.MapGet("/", handler.HandleRequest);
app.Run();
partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
public string HandleRequest()
{
LogHandleRequest(logger);
return "Hello World";
}
[LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
public static partial void LogHandleRequest(ILogger logger);
}
L’exemple précédent :
- Création d’un service singleton appelé
ExampleHandler
et mappage des requêtes web entrantes pour exécuter la fonctionExampleHandler.HandleRequest
. - La ligne 8 définit un constructeur principal pour ExampleHandler, une fonctionnalité ajoutée dans C# 12. L’utilisation du constructeur C# de style plus ancien fonctionnerait également bien, mais nécessite un peu plus de code.
- Le constructeur définit un paramètre de type
ILogger<ExampleHandler>
. ILogger<TCategoryName> dérive de ILogger et indique la catégorie de l’objetILogger
. Le conteneur d’injection de dépendances localise unILogger
avec la catégorie correcte et la fournit comme argument du constructeur. S’il n’existe pas encore deILogger
avec cette catégorie, le conteneur d’injection de dépendances la crée automatiquement à partir duILoggerFactory
dans le fournisseur du service. - Le paramètre
logger
reçu dans le constructeur a été utilisé pour la journalisation dans la fonctionHandleRequest
.
ILoggerFactory fourni par l’hôte
Les générateurs d’hôtes initialisent la configuration par défaut, puis ajoutent un objet configuré ILoggerFactory
au conteneur d’injection de dépendances de l’hôte lors de la génération de l’hôte. Avant la génération de l’hôte, vous pouvez ajuster la configuration de journalisation via HostApplicationBuilder.Logging, WebApplicationBuilder.Logging ou des API similaires sur d’autres hôtes. Les hôtes appliquent également la configuration de journalisation depuis des sources de configuration par défaut comme appsettings.json et des variables d’environnement. Pour plus d’informations, consultez Configuration dans .NET.
Cet exemple part du précédent pour personnaliser le ILoggerFactory
fourni par WebApplicationBuilder
. Il ajoute OpenTelemetry comme fournisseur de journalisation qui transmet les journaux sur OTLP (protocole OpenTelemetry) :
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
Créer un ILoggerFactory avec l’injection de dépendances
Si vous utilisez un conteneur d’injection de dépendances sans hôte, utilisez AddLogging pour configurer et ajouter ILoggerFactory
au conteneur.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
// Add services to the container including logging
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
// Get the ExampleService object from the container
ExampleService service = serviceProvider.GetRequiredService<ExampleService>();
// Do some pretend work
service.DoSomeWork(10, 20);
class ExampleService(ILogger<ExampleService> logger)
{
public void DoSomeWork(int x, int y)
{
logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
}
}
L’exemple précédent :
- Création d’un conteneur de service d’injection de dépendances contenant un
ILoggerFactory
configuré pour écrire sur la console - Ajout d’un singleton
ExampleService
au conteneur - Création d’une instance de
ExampleService
du conteneur d’injection de dépendances qui a également créé automatiquement unILogger<ExampleService>
à utiliser comme argument du constructeur. - Appel de
ExampleService.DoSomeWork
qui a utilisé leILogger<ExampleService>
pour consigner un message sur la console.
Configuration de la journalisation
La configuration de la journalisation est définie dans le code ou via des sources externes, comme des fichiers de configuration et des variables d’environnement. Quand elle est possible, l’utilisation d’une configuration externe est avantageuse, car elle peut être changée sans regénérer l’application. Cependant, certaines tâches, comme la définition des fournisseurs de journalisation, peuvent être configurées seulement à partir du code.
Configurer la journalisation sans code
Pour les apps qui utilisent un hôte, la configuration de la journalisation est généralement fournie par la section "Logging"
des fichiers appsettings.{Environment}
.json. Pour les applications qui n’utilisent pas d’hôte, les sources de configuration externe sont au lieu de cela configurées explicitement ou configurées dans le code.
Le fichier appsettings.Development.json suivant est généré par les modèles de service Worker .NET :
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Dans le code JSON précédent :
- Les catégories de niveau de journal
"Default"
,"Microsoft"
et"Microsoft.Hosting.Lifetime"
sont spécifiées. - La valeur
"Default"
est appliquée à toutes les catégories qui ne sont pas spécifiées, ce qui rendra toutes les valeurs par défaut pour toutes les catégories"Information"
. Vous pouvez remplacer ce comportement en spécifiant une valeur pour une catégorie. - La catégorie
"Microsoft"
s’applique à toutes les catégories qui commencent par"Microsoft"
. - La catégorie
"Microsoft"
journalise au niveau de journalisationWarning
et aux niveaux supérieurs. - La catégorie
"Microsoft.Hosting.Lifetime"
est plus précise que la catégorie"Microsoft"
. La catégorie"Microsoft.Hosting.Lifetime"
journalise donc au niveau"Information"
et aux niveaux supérieurs. - Aucun fournisseur de journaux n’est spécifié.
LogLevel
s’applique donc à tous les fournisseurs de journalisation activés, à l’exception de Windows EventLog.
La propriété Logging
peut avoir des propriétés LogLevel et des propriétés de fournisseur de journaux. La propriété LogLevel
spécifie le niveau de journalisation minimal pour les catégories sélectionnées. Dans le code JSON précédent, les niveaux de journalisation Information
et Warning
sont spécifiés. LogLevel
indique le niveau de gravité du journal, qui peut varier de 0 à 6 :
Trace
= 0, Debug
= 1, Information
= 2, Warning
= 3, Error
= 4, Critical
= 5 et None
= 6.
Quand LogLevel
est spécifié, la journalisation est activée pour les messages au niveau spécifié et aux niveaux supérieurs. Dans le code JSON précédent, la catégorie Default
est journalisée pour Information
et niveaux supérieurs. Par exemple, les messages Information
, Warning
, Error
et Critical
sont journalisés. Si aucun n’est LogLevel
spécifié, la journalisation est définie par défaut sur le niveau Information
. Pour plus d’informations, consultez Niveaux de journalisation.
Une propriété de fournisseur peut spécifier une propriété LogLevel
. Le LogLevel
indiqué sous un fournisseur spécifie les niveaux à journaliser pour ce fournisseur, et remplace les paramètres de journalisation ne concernant pas le fournisseur. Considérez le fichier appsettings.json suivant :
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
Les paramètres de Logging.{ProviderName}.LogLevel
remplacent les paramètres de Logging.LogLevel
. Dans le code JSON précédent, le niveau de journalisation par défaut du fournisseur Debug
est défini sur Information
:
Logging:Debug:LogLevel:Default:Information
Le paramètre précédent spécifie le niveau de journalisation Information
pour chaque catégorie Logging:Debug:
, sauf Microsoft.Hosting
. Lorsqu’une catégorie spécifique est listée, celle-ci remplace la catégorie par défaut. Dans le code JSON précédent, les catégories Logging:Debug:LogLevel
"Microsoft.Hosting"
et "Default"
remplacent les paramètres de Logging:LogLevel
Le niveau de journalisation minimal peut être spécifié pour tous les éléments suivants :
- Certains fournisseurs : par exemple,
Logging:EventSource:LogLevel:Default:Information
- Certaines catégories : par exemple,
Logging:LogLevel:Microsoft:Warning
- Tous les fournisseurs et toutes les catégories :
Logging:LogLevel:Default:Warning
Les journaux situés en dessous du niveau minimal ne seront pas :
- Passés au fournisseur.
- Journalisés ou affichés.
Pour supprimer tous les journaux, spécifiez LogLevel.None. LogLevel.None
a une valeur de 6, ce qui est supérieur à LogLevel.Critical
(5).
Lorsqu’un fournisseur prend en charge les étendues de journal, IncludeScopes
indique si elles sont activées. Pour plus d’informations, consultez Étendues de journalisation.
Le fichier appsettings.json suivant contient des paramètres pour tous les fournisseurs intégrés :
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.Extensions.Hosting": "Warning",
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Microsoft": "Information"
}
},
"EventLog": {
"LogLevel": {
"Microsoft": "Information"
}
},
"AzureAppServicesFile": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Warning"
}
},
"AzureAppServicesBlob": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}
Dans l’exemple précédent :
- Les catégories et les niveaux ne sont pas des valeurs suggérées. L’exemple est fourni pour afficher tous les fournisseurs par défaut.
- Les paramètres de
Logging.{ProviderName}.LogLevel
remplacent les paramètres deLogging.LogLevel
. Par exemple, le niveau dansDebug.LogLevel.Default
remplace le niveau dansLogLevel.Default
. - L’alias de chaque fournisseur est utilisé. Chaque fournisseur définit un alias qui peut être utilisé dans la configuration à la place du nom de type complet. Les alias de fournisseurs intégrés sont les suivants :
Console
Debug
EventSource
EventLog
AzureAppServicesFile
AzureAppServicesBlob
ApplicationInsights
Définir le niveau de journalisation à l’aide de la ligne de commande, de variables d’environnement et d’autres méthodes de configuration
Le niveau de journalisation peut être défini par l’un des fournisseurs de configuration. Par exemple, vous pouvez créer une variable d’environnement persistante nommée Logging:LogLevel:Microsoft
avec la valeur Information
.
Créez et assignez une variable d’environnement persistante, en fonction de la valeur de niveau de journal.
:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M
Dans une nouvelle instance de l’invite de commandes, lisez la variable d’environnement.
:: Prints the env var value
echo %Logging__LogLevel__Microsoft%
Le paramètre d’environnement précédent est conservé dans l’environnement. Pour tester les paramètres lors de l’utilisation d’une application créée avec les modèles de service Worker .NET, utilisez la commande dotnet run
dans le répertoire du projet après l’affectation de la variable d’environnement.
dotnet run
Conseil
Après avoir défini une variable d’environnement, redémarrez votre environnement de développement intégré (IDE) pour vous assurer que les variables d’environnement nouvellement ajoutées sont disponibles.
Dans Azure App Service, sélectionnez Nouveau paramètre d’application dans la page Paramètres > Configuration. Les paramètres d’application Azure App Service sont :
- Chiffrés au repos et transmis sur un canal chiffré.
- Exposés en tant que variables d’environnement.
Pour plus d’informations sur la définition de valeurs de configuration .NET à l’aide de variables d’environnement, consultez Variables d’environnement.
Configurer la journalisation avec du code
Pour configurer la journalisation dans du code, utilisez l’API ILoggingBuilder. Vous pouvez y accéder depuis différents endroits :
- Quand vous créez le
ILoggerFactory
directement, configurez dans LoggerFactory.Create. - Quand vous utilisez l’injection de dépendances sans hôte, configurez dans LoggingServiceCollectionExtensions.AddLogging.
- Quand vous utilisez un hôte, configurez avec HostApplicationBuilder.Logging, WebApplicationBuilder.Logging ou d’autres API spécifiques à l’hôte.
Cet exemple montre comment définir le fournisseur de journalisation de la console et plusieurs filtres.
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(static builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogDebug("Hello {Target}", "Everyone");
Dans l’exemple précédent, AddFilter est utilisé pour ajuster le niveau de journalisation qui est activé pour différentes catégories. AddConsole est utilisé pour ajouter le fournisseur de journalisation de la console. Par défaut, les journaux avec la gravité Debug
ne sont pas activés, mais comme la configuration a ajusté les filtres, le message de débogage « Hello Everyone » s’affiche sur la console.
Mode d’application des règles de filtre
À la création d’un objet ILogger<TCategoryName>, l’objet ILoggerFactory sélectionne une seule règle à appliquer à cet enregistrement d’événements par fournisseur. Tous les messages écrits par une instance ILogger
sont filtrés selon les règles sélectionnées. La règle la plus précise qui peut être appliquée à chaque paire catégorie/fournisseur est sélectionnée parmi les règles disponibles.
L’algorithme suivant est utilisé pour chaque fournisseur quand un objet ILogger
est créé pour une catégorie donnée :
- Sélectionnez toutes les règles qui correspondent au fournisseur ou à son alias. Si aucune correspondance n’est trouvée, sélectionnez toutes les règles avec un fournisseur vide.
- À partir du résultat de l’étape précédente, sélectionnez les règles ayant le plus long préfixe de catégorie correspondant. Si aucune correspondance n’est trouvée, sélectionnez toutes les règles qui ne spécifient pas de catégorie.
- Si plusieurs règles sont sélectionnées, prenez la dernière.
- Si aucune règle n’est sélectionnée, utilisez LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) pour spécifier le niveau de journalisation minimale.
Catégorie de journal
Quand un objet ILogger
est créé, une catégorie est spécifiée. Cette catégorie est incluse dans tous les messages de journal créés par cette instance de ILogger
. La chaîne de la catégorie est arbitraire, mais la convention est d’utiliser le nom complet de la classe. Par exemple, dans une application avec un service défini comme l’objet suivant, la catégorie peut être "Example.DefaultService"
:
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger<DefaultService> _logger;
public DefaultService(ILogger<DefaultService> logger) =>
_logger = logger;
// ...
}
}
SSi une catégorisation plus poussée est souhaitée, la convention consiste à utiliser un nom hiérarchique en ajoutant une sous-catégorie au nom de classe complet, et à spécifier explicitement la catégorie à l’aide de LoggerFactory.CreateLogger :
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger _logger;
public DefaultService(ILoggerFactory loggerFactory) =>
_logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");
// ...
}
}
L’appel de CreateLogger
avec un nom fixe peut être utile si vous l’utilisez dans plusieurs méthodes afin que les événements puissent être organisés par catégorie.
ILogger<T>
équivaut à appeler CreateLogger
avec le nom de type complet T
.
Log level
Le tableau suivant liste les valeurs LogLevel, la méthode d’extension de commodité Log{LogLevel}
, ainsi que des suggestions d’utilisation :
LogLevel | Valeur | Méthode | Description |
---|---|---|---|
Trace | 0 | LogTrace | Contiennent les messages les plus détaillés. Ces messages peuvent contenir des données d’application sensibles. Ils sont désactivés par défaut et ne doivent pas être activés dans un environnement de production. |
Déboguer | 1 | LogDebug | Pour le débogage et le développement. À utiliser avec prudence dans un environnement de production en raison du volume élevé de messages. |
Information | 2 | LogInformation | Effectue le suivi du flux général de l’application. Peut avoir une valeur à long terme. |
Avertissement | 3 | LogWarning | Pour les événements anormaux ou inattendus. Comprend généralement des erreurs ou des conditions qui n’entraînent pas l’échec de l’application. |
Error | 4 | LogError | Fournit des informations sur des erreurs et des exceptions qui ne peuvent pas être gérées. Ces messages indiquent un échec de l’opération ou de la demande en cours, et non l’échec de l’application. |
Critical | 5 | LogCritical | Fournit des informations sur des échecs qui nécessitent un examen immédiat. Exemples : perte de données, espace disque insuffisant. |
Aucun | 6 | Spécifie qu’aucun message ne doit être écrit. |
Dans le tableau précédent, LogLevel
est listé du plus faible niveau de gravité au plus élevé.
Le premier paramètre de la méthode Log (LogLevel) indique le niveau de gravité du journal. Au lieu d’appeler Log(LogLevel, ...)
, la plupart des développeurs appellent les méthodes d’extension Log{LogLevel}. Les méthodes d’extension Log{LogLevel}
appellent la méthode Log
et spécifient le LogLevel
. Par exemple, les deux appels de journalisation suivants ont un fonctionnement équivalent et produisent le même journal :
public void LogDetails()
{
var logMessage = "Details for log.";
_logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
_logger.LogInformation(AppLogEvents.Details, logMessage);
}
AppLogEvents.Details
est l’ID d’événement et est implicitement représenté par une valeur de constante Int32. AppLogEvents
est une classe qui expose diverses constantes d’identificateur nommées et qui s’affiche dans la section ID d’événement du journal.
Le code suivant crée des journaux Information
et Warning
:
public async Task<T> GetAsync<T>(string id)
{
_logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
return result;
}
Dans le code précédent, le premier Log{LogLevel}
paramètre, AppLogEvents.Read
est l’ID d’événement du journal. Le deuxième paramètre est un modèle de message contenant des espaces réservés pour les valeurs d’argument fournies par les autres paramètres de méthode. Les paramètres de méthode sont expliqués dans la section Modèle de message plus loin dans cet article.
Configurez le niveau de journal approprié et appelez les méthodes Log{LogLevel}
appropriées pour contrôler la quantité de sortie du journal écrite sur un support de stockage particulier. Exemple :
- En production :
- La journalisation aux niveaux
Trace
ouDebug
produit un volume élevé de messages de journal détaillés. Pour contrôler les coûts et ne pas dépasser les limites de stockage des données, journalisez les messages de niveauTrace
etDebug
dans un magasin de données à haut volume et à faible coût. Il est conseillé de limiterTrace
etDebug
à certaines catégories. - La journalisation aux niveaux allant de
Warning
àCritical
doivent produire moins de messages de journal.- Les coûts et les limites de stockage ne posent généralement pas problème.
- Peu de journaux permettent une plus grande flexibilité au niveau du choix du magasin de données.
- La journalisation aux niveaux
- Lors du développement :
- Réglez sur
Warning
. - Ajoutez les messages
Trace
ouDebug
lorsque vous résolvez des problèmes. Pour limiter la sortie, définissezTrace
ouDebug
uniquement pour les catégories en cours d’investigation.
- Réglez sur
Le code JSON suivant définit Logging:Console:LogLevel:Microsoft:Information
:
{
"Logging": {
"LogLevel": {
"Microsoft": "Warning"
},
"Console": {
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
ID d’événement de log
Chaque journal peut spécifier un identificateur d’événement, le EventId est une structure avec un Id
et des propriétés en lecture seule facultatives Name
. L’exemple de code source utilise la classe AppLogEvents
pour définir les ID d’événement :
using Microsoft.Extensions.Logging;
internal static class AppLogEvents
{
internal static EventId Create = new(1000, "Created");
internal static EventId Read = new(1001, "Read");
internal static EventId Update = new(1002, "Updated");
internal static EventId Delete = new(1003, "Deleted");
// These are also valid EventId instances, as there's
// an implicit conversion from int to an EventId
internal const int Details = 3000;
internal const int Error = 3001;
internal static EventId ReadNotFound = 4000;
internal static EventId UpdateNotFound = 4001;
// ...
}
Conseil
Pour plus d’informations sur la conversion d’un int
en un EventId
, consultez Opérateur EventId.Implicit(Int32 en EventId).
Un ID d’événement associe un jeu d’événements. Par exemple, tous les journaux liés à la lecture des valeurs d’un référentiel peuvent être 1001
.
Le fournisseur de journalisation peut journaliser l’ID d’événement dans un champ ID, dans le message de journalisation, ou pas du tout. Le fournisseur Debug n’affiche pas les ID d’événements. Le fournisseur Console affiche les ID d’événements entre crochets après la catégorie :
info: Example.DefaultService.GetAsync[1001]
Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
GetAsync(a1b2c3) not found
Certains fournisseurs de journalisation stockent l’ID d’événement dans un champ, ce qui permet de filtrer en fonction de l’ID.
Modèle de message de journal
Chaque API de journalisation utilise un modèle de message. Ce dernier peut contenir des espaces réservés pour lesquels les arguments sont fournis. Utilisez des noms et non des nombres pour les espaces réservés. L’ordre des espaces réservés, pas leurs noms, détermine quels paramètres sont utilisés pour fournir leurs valeurs. Dans le code suivant, les noms de paramètres sont hors séquence dans le modèle de message :
string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);
Le code précédent crée un message de journal avec les valeurs des paramètres dans la séquence :
Parameter values: param1, param2
Notes
Soyez attentif lorsque vous utilisez plusieurs espaces réservés dans un seul modèle de message, car ils sont basés sur des ordinaux. Les noms ne sont pas utilisés pour aligner les arguments sur les espaces réservés.
Cette approche permet aux fournisseurs de journalisation d’implémenter une journalisation sémantique ou structurée. Les arguments proprement dits, et pas seulement le modèle de message mis en forme, sont transmis au système de journalisation. Cela permet aux fournisseurs de journalisation de stocker les valeurs des paramètres sous forme de champs. Tenez compte de la méthode d’enregistreur d’événements suivante :
_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);
Par exemple, lorsque vous journalisez des événements vers le Stockage Table Azure :
- Chaque entité de table Azure peut avoir des propriétés
ID
etRunTime
. - Les tables avec des propriétés simplifient les requêtes qui sont exécutées sur des données journalisées. Par exemple, vous pouvez rechercher tous les journaux compris dans une plage
RunTime
spécifique, sans avoir besoin d’analyser le délai d’expiration du message texte.
Mise en forme du modèle de message de journal
Les modèles de messages de journal prennent en charge la mise en forme des espaces réservés. Les modèles sont libres de spécifier n’importe quel format valide pour l’argument de type donné. Prenons l’exemple de le modèle de message d’enregistreur d’événements Information
suivant :
_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022
Dans l’exemple précédent, l’instance DateTimeOffset
est le type qui correspond au PlaceHolderName
dans le modèle de message d’enregistreur d’événements. Ce nom peut être n’importe quel nom, car les valeurs sont basées sur des ordinaux. Le format MMMM dd, yyyy
est valide pour le type DateTimeOffset
.
Pour plus d’informations sur la mise en forme de DateTime
et DateTimeOffset
, consultez Chaînes de format de date et d’heure personnalisées.
Exemples
Les exemples suivants montrent comment mettre en forme un modèle de message à l’aide de la syntaxe d’espace réservé {}
. En outre, un exemple d’échappement de la syntaxe d’espace réservé {}
s’affiche avec sa sortie. Enfin, l’interpolation de chaîne avec des espaces réservés de création de modèles est également affichée :
logger.LogInformation("Number: {Number}", 1); // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3); // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5); // {Number}: 5
Conseil
- Dans la plupart des cas, vous devez utiliser la mise en forme du modèle de message de journal pour la journalisation. L’utilisation de l’interpolation de chaîne peut entraîner des problèmes de performances.
- La règle d’analyse du code CA2254 : Le modèle doit être une expression statique vous avertit des endroits où vos messages de journal n’utilisent pas la mise en forme appropriée.
Enregistrer des exceptions
Les méthodes d’enregistreur d’événements ont des surcharges qui prennent un paramètre d’exception :
public void Test(string id)
{
try
{
if (id is "none")
{
throw new Exception("Default Id detected.");
}
}
catch (Exception ex)
{
_logger.LogWarning(
AppLogEvents.Error, ex,
"Failed to process iteration: {Id}", id);
}
}
La journalisation des exceptions dépend du fournisseur.
Niveau de journalisation par défaut
Si le niveau de journalisation par défaut n’est pas défini, la valeur sera Information
.
Par exemple, considérez l’application de service Worker suivante :
- Créé avec les modèles Worker .NET.
- appsettings.json and appsettings.Development.json supprimés ou renommés.
Avec la configuration précédente, l’accès à la page de confidentialité ou d’accueil produit de nombreux messages Trace
, Debug
et Information
de catégorie Microsoft
.
Le code suivant définit le niveau de journalisation par défaut lorsque celui-ci n’est pas défini dans la configuration :
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.SetMinimumLevel(LogLevel.Warning);
using IHost host = builder.Build();
await host.RunAsync();
fonction Filter
Une fonction de filtre est appelée pour tous les fournisseurs et toutes les catégories auxquels aucune règle n’est affectée dans la configuration ou dans le code :
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddFilter((provider, category, logLevel) =>
{
return provider.Contains("ConsoleLoggerProvider")
&& (category.Contains("Example") || category.Contains("Microsoft"))
&& logLevel >= LogLevel.Information;
});
using IHost host = builder.Build();
await host.RunAsync();
Le code précédent affiche les journaux de console lorsque la catégorie contient Example
ou Microsoft
, et si le niveau de journalisation est Information
ou supérieur.
Étendues de journal
Une étendue regroupe un ensemble d’opérations logiques. Ce regroupement permet de joindre les mêmes données à tous les journaux créés au sein d’un ensemble. Par exemple, chaque journal créé dans le cadre du traitement d’une transaction peut contenir l’ID de la transaction.
Une étendue :
- Est un type IDisposable retourné par la méthode BeginScope.
- Dure jusqu’à sa suppression.
Les fournisseurs suivants prennent en charge les étendues :
Utilisez une étendue en incluant les appels de l’enregistrement d’événements dans un bloc using
:
public async Task<T> GetAsync<T>(string id)
{
T result;
var transactionId = Guid.NewGuid().ToString();
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TransactionId", transactionId),
}))
{
_logger.LogInformation(
AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(
AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
}
return result;
}
Le JSON suivant active les étendues pour le fournisseur Console :
{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Warning",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}
Le code suivant active les étendues pour le fournisseur Console :
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);
using IHost host = builder.Build();
await host.RunAsync();
Créer des journaux dans Main
Le code suivant journalise dans Main
en obtenant une instance ILogger
à partir d’une injection de dépendances après avoir créé l’hôte :
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using IHost host = Host.CreateApplicationBuilder(args).Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Host created.");
await host.RunAsync();
Le code précédent s’appuie sur deux packages NuGet :
Son fichier projet se présente comme suit :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup>
</Project>
Aucune méthode d’enregistreur d’événements asynchrone
La journalisation doit être suffisamment rapide par rapport au coût du code asynchrone en matière de performances. Si un magasin de données de journalisation est lent, n’écrivez pas de données directement dedans. Écrivez les messages de journal dans un magasin rapide, puis déplacez-les vers le magasin lent. Par exemple, lorsque vous vous connectez à SQL Server, ne le faites pas directement dans une méthode Log
, puisque les méthodes Log
sont synchrones. Ajoutez plutôt de façon synchronisée des messages de journal à une file d’attente en mémoire, puis configurez un traitement en arrière-plan afin d’extraire les messages de la file d’attente et d’effectuer le travail asynchrone d’envoi des données vers SQL Server.
Modifier les niveaux de journalisation dans une application en cours d’exécution
L’API de journalisation ne permet pas de modifier les niveaux de journalisation pendant l’exécution d’une application. Toutefois, certains fournisseurs de configuration sont capables de recharger la configuration, ce qui agit immédiatement sur la configuration de la journalisation. Par exemple, le fournisseur de configuration de fichier recharge la configuration de journalisation par défaut. Si la configuration est modifiée dans le code pendant l’exécution d’une application, celle-ci peut appeler IConfigurationRoot.Reload pour mettre à jour sa configuration de journalisation.
Packages NuGet
Les interfaces ILogger<TCategoryName> et ILoggerFactory ainsi que les implémentations sont incluses dans la plupart des kits SDK .NET comme référence de package implicite. Elles sont également disponibles explicitement dans les packages NuGet suivants lorsqu’elles ne sont pas référencées implicitement :
- Les interfaces se trouvent dans Microsoft.Extensions.Logging.Abstractions.
- Les implémentations par défaut se trouvent dans Microsoft.Extensions.Logging.
Pour plus d’informations sur les références de package implicites du Kit de développement logiciel (SDK) .NET, consultez le Kit de développement logiciel (SDK) .NET : table vers espace de noms implicite.
Voir aussi
- Fournisseurs de journalisation dans .NET
- Implémenter un fournisseur de journalisation personnalisé dans .NET
- Mise en forme des journaux de la console
- Journalisation hautes performances dans .NET
- Conseils de journalisation pour les créateurs de bibliothèques .NET
- Les bogues de journalisation doivent être créés dans le référentiel github.com/dotnet/runtime