Introduction au journal des événements Windows et à ETW — placer les journaux de votre application métier sur les mécanismes standard de l'OS

· · CSharp, .NET, Journal des événements, ETW, EventSource, Conception des journaux, Enquête de bogue, Développement Windows

Lorsqu’on développe des applications métier ou services Windows en C#, on produit déjà généralement des journaux quotidiens avec un enregistreur de fichiers maison, Serilog ou NLog. Cela ne rend ni le journal des événements Windows ni ETW (Event Tracing for Windows) inutiles. Ils ne sont pas des « remplaçants du journal de fichiers » : ce sont des enregistrements visibles à une autre couche, par l’exploitation et les outils standard de l’OS.

Cet article présente la répartition entre journal de fichiers, journal des événements et ETW, leur implémentation .NET, le minimum utile d’ETW, la collecte et l’investigation, ainsi que les pièges courants.

1. L’essentiel d’abord

  • Les trois mécanismes ne se remplacent pas : ils se répartissent le travail. Le journal de fichiers sert aux développeurs pour remonter les détails ; le journal des événements sert à l’exploitation et aux outils standard tels qu’Event Viewer ; ETW est une trace à haute fréquence activée à la demande pour les performances ou les bogues difficiles à reproduire. Utilisez-les ensemble, avec un niveau d’information différent.
  • N’écrivez dans le journal des événements que « démarrage, arrêt et erreurs fatales ». Y envoyer chaque ligne noie les anomalies dont l’exploitation a besoin. Conservez le détail dans le fichier et réduisez le flux vers le journal des événements.
  • L’enregistrement d’une source d’événement exige les privilèges administrateur. Depuis Windows Vista, EventLog.CreateEventSource ne peut pas être appelé sans ces privilèges. N’enregistrez pas la source au premier accès à l’exécution : faites-le dans l’installeur. 1
  • ETW n’est pas une collecte toujours active. Même avec un EventSource, rien n’est enregistré sans abonné, dotnet-trace, PerfView ou outil ETW. Ciblez le fournisseur et collectez seulement pendant l’analyse de performances ou d’un bogue précis. 2
  • Écrivez les messages sous forme de modèles, pas par concaténation ou interpolation. logger.LogInformation("La commande {OrderId} a échoué", orderId) conserve OrderId comme clé de recherche de journal structuré, contrairement à la concaténation ou à l’interpolation. 3
  • Le gonflement des journaux vient souvent de la petite limite par défaut. La taille maximale par défaut du journal des événements n’est que de 512 KB. Agrandissez-la explicitement et décidez dès la conception si les anciennes entrées sont écrasées ou les nouvelles rejetées. 4
Aspect Journal de fichiers Journal des événements ETW
Lecteur principal Développeurs et mainteneurs Exploitation, supervision, Event Viewer Investigateurs de performances, support
Volume Détaillé, Trace/Debug compris Faible : démarrage, arrêt, erreurs fatales Élevé seulement lorsqu’il est activé ; rien par défaut
Durée / rétention Facile à conserver selon la rotation Les entrées anciennes sont écrasées ou les nouvelles rejetées à la limite Portée de session, enregistrée en .etl/.nettrace après collecte
Intégration OS Aucune, visionneuse propre nécessaire Event Viewer, wevtutil, Get-WinEvent dotnet-trace, PerfView, WPR/WPA
Privilèges Droit d’écriture de l’utilisateur de l’application Enregistrer une source exige l’administrateur ; écrire dans une source enregistrée fonctionne sous utilisateur standard Démarrer une collecte exige souvent l’administrateur

2. Bases du journal des événements Windows

Windows possède par défaut les journaux Application, System et Security, ce dernier étant en lecture seule. Les applications et services métier écrivent dans Application ou un canal propre ; par convention, les pilotes écrivent dans System. 5

L’unité d’écriture est la « source d’événement », un nom identifiant l’application, enregistré à l’avance par EventLog.CreateEventSource. Depuis Windows Vista, cet enregistrement exige les privilèges administrateur, car il faut vérifier l’unicité du nom dans tous les journaux, y compris Security. 1

using System.Diagnostics;

// À exécuter depuis un programme d'installation ou un processus de configuration, tant que les privilèges administrateur sont disponibles
const string SourceName = "KomuraSoft.OrderService";
const string LogName = "Application";

if (!EventLog.SourceExists(SourceName))
{
    EventLog.CreateEventSource(SourceName, LogName);
}

Créer la source au premier accès échoue lorsque l’application de production tourne sous utilisateur standard. La conception sûre est donc : l’installeur enregistre la source et l’application écrit seulement dans cette source déjà enregistrée. Chaque événement a un niveau, EventLogEntryType, et un ID d’événement entier défini par l’application, utile pour rechercher et agréger le même type d’incident.

3. Écrire dans le journal des événements depuis .NET

Deux voies principales existent.

3.1 Employer directement System.Diagnostics.EventLog

Cette API de bas niveau convient lorsqu’un contrôle précis est nécessaire, par exemple catégories ou données binaires. Sur .NET, contrairement à .NET Framework, ajoutez le package NuGet System.Diagnostics.EventLog.

using System.Diagnostics;

public sealed class OrderServiceEventLog
{
    private const string SourceName = "KomuraSoft.OrderService";

    public void WriteServiceStarted()
    {
        EventLog.WriteEntry(SourceName, "OrderService a démarré.",
            EventLogEntryType.Information, eventID: 1000);
    }

    public void WriteFatalError(Exception ex)
    {
        EventLog.WriteEntry(SourceName,
            $"Une erreur fatale s'est produite et le traitement ne peut pas continuer. Consultez le journal de fichiers pour plus de détails. {ex.GetType().Name}: {ex.Message}",
            EventLogEntryType.Error, eventID: 1999);
    }
}

3.2 Employer ILogger + AddEventLog

Si la journalisation structurée repose déjà sur ILogger, AddEventLog du package Microsoft.Extensions.Logging.EventLog s’intègre naturellement au pipeline. Dans ASP.NET Core et les Worker Services Generic Host sous Windows, le fournisseur EventLog est activé par défaut. 6

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddEventLog(settings =>
{
    settings.SourceName = "KomuraSoft.OrderService";
    settings.LogName = "Application";
    // En plus des filtres de catégorie par défaut, n'acheminer que Warning et les niveaux supérieurs
    // vers le journal des événements (laisser les détails au journal de fichiers)
    settings.Filter = (_, level) => level >= LogLevel.Warning;
});

using IHost host = builder.Build();
await host.RunAsync();

Sans EventLogSettings, LogName vaut par défaut "Application" et SourceName ".NET Runtime". 7 Définissez toujours explicitement SourceName, sinon Event Viewer ne permet pas de distinguer votre application des autres applications .NET. Pour un traitement par lot, un service Windows ou le Planificateur de tâches, conservez dans le journal des événements seulement démarrage, arrêt et erreur fatale, et laissez le détail dans le fichier. L’exploitation des services et des tâches est décrite dans Building and Operating Windows Services et When a Task Scheduler Task Doesn’t Run, or Exits with 0x1.

4. Qu’est-ce qu’ETW ? — une base de traçage traversant tout l’OS

Event Tracing for Windows (ETW) permet d’instrumenter noyau et applications utilisateur sur une fondation commune. Sa grande force est d’enregistrer et analyser, sur la même ligne de temps, les événements noyau tels que les E/S disque ou création de processus, et les événements personnalisés de l’application. 8

Dans .NET, créez votre propre fournisseur ETW avec une classe dérivant de System.Diagnostics.Tracing.EventSource. ETW fonctionne en publication-abonnement : aucun événement n’est enregistré sans abonné. Implémenter EventSource ne suffit donc pas ; la collecte doit l’activer explicitement. 2

using System.Diagnostics.Tracing;

[EventSource(Name = "KomuraSoft-OrderService")]
internal sealed class OrderServiceEventSource : EventSource
{
    public static readonly OrderServiceEventSource Log = new();

    [Event(1, Level = EventLevel.Informational, Message = "Le traitement de la commande a démarré. OrderId={0}")]
    public void OrderProcessingStart(string orderId)
    {
        if (IsEnabled())
        {
            WriteEvent(1, orderId);
        }
    }

    [Event(2, Level = EventLevel.Informational, Message = "Le traitement de la commande est terminé. OrderId={0}")]
    public void OrderProcessingStop(string orderId)
    {
        if (IsEnabled())
        {
            WriteEvent(2, orderId);
        }
    }
}

Le code appelant l’emploie ainsi. Le test IsEnabled() évite une surcharge inutile quand personne ne collecte.

OrderServiceEventSource.Log.OrderProcessingStart(order.Id);
try
{
    ProcessOrder(order);
}
finally
{
    OrderServiceEventSource.Log.OrderProcessingStop(order.Id);
}

La collecte la plus simple utilise dotnet-trace, outil multiplateforme. 9

dotnet-trace collect --providers KomuraSoft-OrderService -- OrderService.exe

Ouvrez le .nettrace dans Visual Studio ou PerfView. Pour une enquête de performances plus poussée, notamment avec événements noyau, employez Windows Performance Recorder (WPR) du Windows ADK puis Windows Performance Analyzer (WPA). 10 Pour développer et maintenir une application métier, définir le fournisseur avec EventSource et le collecter avec dotnet-trace est le minimum suffisant. Les procédures d’analyse PerfView sont détaillées dans l’article PerfView et dotnet-trace.

5. Pratique de la journalisation structurée

Les modèles de message ILogger sont des chaînes fixes à espaces réservés nommés, tels que {PlaceHolder}. Ce n’est pas qu’une convention : garder le modèle inchangé et passer les arguments séparément conserve la correspondance entre noms et valeurs, donc les données structurées. 11

// Recommandé : conserver un modèle fixe et passer les arguments séparément
logger.LogWarning("Impossible de réserver le stock pour la commande {OrderId}. Entrepôt={WarehouseId}", orderId, warehouseId);

// Non recommandé : la concaténation ou l'interpolation de chaînes rompt la correspondance entre le modèle et ses valeurs
logger.LogWarning("La commande " + orderId + " n'a pas pu réserver le stock. Entrepôt=" + warehouseId);
logger.LogWarning($"Impossible de réserver le stock pour la commande {orderId}. Entrepôt={warehouseId}");

La règle d’analyseur CA2254 détecte cette forme déconseillée dont le modèle change à chaque appel. 3 Sur les chemins très fréquents, la génération de source avec LoggerMessageAttribute évite aussi le coût du boxing et de l’analyse du modèle. 12

using Microsoft.Extensions.Logging;

internal static partial class Log
{
    [LoggerMessage(
        EventId = 2001,
        Level = LogLevel.Warning,
        Message = "Impossible de réserver le stock pour la commande {OrderId}. Entrepôt={WarehouseId}")]
    public static partial void StockReservationFailed(
        this ILogger logger, string orderId, string warehouseId);
}

// Côté appelant
logger.StockReservationFailed(orderId, warehouseId);

Une même entrée peut aller vers le fichier en détail, vers un journal des événements réduit et vers ETW à faible coût via EventSourceLoggerProvider. Enregistrez plusieurs ILoggerProvider et faites varier le niveau par fournisseur avec Filter. Voir Minimum Requirements for a Custom Logger and an Integration Test Checklist et Designing Windows Apps to Preserve Logs and Dumps on Crash.

6. Collecte et investigation — lire les journaux écrits

Dans Event Viewer, « Filter » ou « Custom Views » extrait une source, un niveau ou un ID précis. Enregistrez les conditions fréquentes comme vue personnalisée. Les vues utilisent XPath ; cette requête sélectionne une source particulière. 13

<QueryList>
  <Query Id="0" Path="Application">
    <Select Path="Application">
      *[System[Provider[@Name='KomuraSoft.OrderService'] and (Level=2 or Level=3)]]
    </Select>
  </Query>
</QueryList>

wevtutil permet en ligne de commande d’exporter les journaux et de modifier leurs paramètres. Lors d’un incident, exportez la fenêtre concernée et transmettez-la au support. 14

:: Exporter l'intégralité du journal Application dans un fichier evtx
wevtutil epl Application C:\logs\application_20260707.evtx

:: Afficher uniquement les événements d'une source précise (les 10 plus récents)
wevtutil qe Application /q:"*[System[Provider[@Name='KomuraSoft.OrderService']]]" /c:10 /rd:true /f:text

Pour un crash, rapprochez par horodatage le journal des événements, le journal de fichiers et le dump. À partir d’une erreur fatale du journal, comparez les détails du fichier et le dump WER/ProcDump. Voir An Introduction to Collecting Windows Crash Dumps — WER/ProcDump/WinDbg et Reading a Crash Dump with WinDbg + SOS.

7. Pièges

  • Écriture avec une source non enregistrée. L’événement finit dans Application, mais sans DLL de ressources de messages, Event Viewer ne peut pas afficher sa description. Ne tentez jamais l’enregistrement automatique par CreateEventSource à l’exécution sous utilisateur non administrateur ; terminez l’enregistrement dans l’installeur. 15
  • Confusion sur l’écriture sous un autre nom de source. Avec le droit d’écriture, le journal accepte même une source jamais enregistrée par l’application. L’OS ne force pas l’unicité d’usage ; une source trop générique peut entrer en collision avec l’application d’un autre fournisseur. 5
  • Gonflement et rétention. La limite par défaut n’est que de 512 KB. 4 Agrandissez-la avec wevtutil sl ou l’installeur et décidez explicitement d’écraser les anciennes entrées ou de rejeter les nouvelles. OverwriteOlder est obsolète et peut se comporter comme « ne pas écraser ». 16
  • Affichage dans des environnements multilingues. Avec des fichiers de messages localisés (MessageResourceFile), un serveur sans la DLL ou avec une version différente peut afficher « The description for Event ID XX cannot be found. ». Écrire directement des chaînes avec WriteEntry, sans fichier de ressources, évite ce problème.
  • Échecs par privilèges insuffisants. Enregistrer une source exige l’administrateur, mais écrire dans une source enregistrée fonctionne sous utilisateur standard. Distinguez toujours l’opération qui exige réellement l’élévation au lieu d’exécuter toute l’application en administrateur.

Articles connexes

Domaines de conseil associés

KomuraSoft LLC assure du conseil technique pour concevoir les fondations de journalisation, de journal des événements et d’ETW des applications métier Windows, ainsi que les points d’observation utiles à l’investigation des défaillances.

Références

  1. Microsoft Learn, EventLog.CreateEventSource Method. La création d’une source exige l’administrateur depuis Vista, car elle recherche tous les journaux, y compris Security.  2

  2. Microsoft Learn, Getting Started with EventSource. Modèle publication-abonnement d’EventSource et collecte par dotnet-trace collect 2

  3. Microsoft Learn, CA2254: Template should be a static expression. Concaténation et interpolation font perdre la correspondance entre noms d’espaces réservés et valeurs.  2

  4. Microsoft Learn, EventLog.MaximumKilobytes Property. Taille maximale par défaut de 512 kilooctets.  2

  5. Microsoft Learn, EventLog Class. Les journaux par défaut sont Application/System/Security, une source doit être unique sur un ordinateur et toute source enregistrée peut être écrite si le droit d’écriture est présent.  2

  6. Microsoft Learn, Logging providers in .NET. Les fournisseurs par défaut du Generic Host incluent Console, Debug, EventSource et EventLog sous Windows. 

  7. Microsoft Learn, EventLogSettings Class. Valeurs par défaut AddEventLog : Application et .NET Runtime

  8. Microsoft Learn, Event Tracing. ETW permet de démarrer, arrêter, instrumenter et consommer les événements de trace d’une application. 

  9. Microsoft Learn, dotnet-trace performance analysis utility. dotnet-trace est un outil multiplateforme de collecte basé sur EventPipe. 

  10. Microsoft Learn, Introduction to WPR. WPR étend ETW pour enregistrer finement le système et les applications. 

  11. Microsoft Learn, Logging in C# and .NET. Syntaxe {PlaceHolder} et conversion des noms de clés en propriétés du journal. 

  12. Microsoft Learn, High-performance logging in .NET. La journalisation générée par source via LoggerMessageAttribute évite le boxing et l’analyse des modèles. 

  13. Microsoft Learn, Comparison of ETW and EventLog logger functionality. Création d’une vue personnalisée Event Viewer avec une requête XPath filtrant le nom d’événement. 

  14. Microsoft Learn, wevtutil. Opérations de ligne de commande : export epl, requête qe et réglages sl

  15. Microsoft Learn, Event Sources. Une source non enregistrée retombe dans Application, mais sans fichier de messages Event Viewer ne peut pas afficher la description. 

  16. Microsoft Learn, OverflowAction Enum. Comportement à la limite de taille et obsolescence de OverwriteOlder. 

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Si j'ai déjà des journaux de fichiers, ai-je encore besoin du journal des événements ?
Pour une enquête détaillée, les journaux de fichiers suffisent souvent. En revanche, si vous voulez que l'exploitation repère les problèmes via l'Observateur d'événements ou des outils de supervision standard, par exemple les échecs du Planificateur de tâches ou SCOM, il est pratique d'écrire aussi les points essentiels dans le journal des événements. Les deux ne se remplacent pas : ce sont des enregistrements de couches et de lecteurs différents.
Comment définir les règles de numérotation des identifiants d'événement ?
Il n'existe pas une règle unique, mais trois règles pratiques réduisent les erreurs : découper les plages par domaine fonctionnel, par exemple les 1000 pour le démarrage et les 2000 pour les communications ; ne jamais changer le sens d'un ID déjà émis ni le réutiliser ; accepter les trous dans la numérotation. L'EventId de ILogger sert aussi de clé de recherche de journaux structurés, donc un schéma facile à documenter facilite la maintenance.
J'utilise Serilog ou NLog : quel est le lien avec cet article ?
Serilog et NLog sont des implémentations sous ILogger qui envoient les mêmes entrées vers plusieurs destinations : fichiers, journal des événements, ETW ou SaaS externe. Les principes expliqués ici — limiter le journal des événements aux éléments essentiels et ne pas casser le modèle de message — s'appliquent dans les deux cas. Employer une destination dédiée comme Serilog.Sinks.EventLog ou AddEventLog est un choix d'implémentation.
Quelle part d'ETW dois-je apprendre ?
Pour développer et maintenir une application métier, il suffit de savoir définir son fournisseur avec EventSource et collecter ses événements avec dotnet-trace. L'analyse de piles dans PerfView et la collecte d'événements noyau avec WPR peuvent venir plus tard, lorsqu'on se spécialise dans l'investigation de performances ; cet article les laisse volontairement hors de son périmètre.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog