Créer un journal de génération

Les enregistreurs d’événements vous permettent de personnaliser la sortie de votre génération et d’afficher des messages, des erreurs ou des avertissements en réponse à des événements de génération spécifiques. Chaque enregistreur d’événements est implémenté en tant que classe .NET qui met en œuvre l’interface ILogger, définie dans l’assembly Microsoft.Build.Framework.dll.

Vous pouvez adopter deux approches lors de l’implémentation d’un enregistreur d’événements :

  • implémenter directement l’interface ILogger ;

  • dériver votre classe de la classe d’assistance, Logger, définie dans l’assembly Microsoft.Build.Utilities.dll. Logger implémente ILogger et fournit des implémentations par défaut de certains membres ILogger.

    Cette rubrique explique comment écrire un enregistreur d’événements simple qui dérive de Logger et affiche des messages sur la console en réponse à certains événements de build.

S’inscrire aux événements

L’objectif d’un enregistreur d’événements consiste à rassembler des informations sur la progression de la génération telles que signalées par le moteur de génération, puis à signaler ces informations de manière utile. Tous les enregistreurs d’événements doivent substituer la méthode Initialize, où l’enregistreur d’événements enregistre les événements. Dans cet exemple, l’enregistreur d’événements enregistre les événements TargetStarted, ProjectStarted et ProjectFinished.

public class MySimpleLogger : Logger
{
    public override void Initialize(Microsoft.Build.Framework.IEventSource eventSource)
    {
        //Register for the ProjectStarted, TargetStarted, and ProjectFinished events
        eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted);
        eventSource.TargetStarted += new TargetStartedEventHandler(eventSource_TargetStarted);
        eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished);
    }

Répondre aux événements

Maintenant que l’enregistreur d’événements est inscrit pour des événements spécifiques, il doit gérer ces événements quand ils se produisent. Pour les événements ProjectStarted et ProjectFinished, l’enregistreur d’événements écrit simplement une courte expression et le nom du fichier projet impliqué dans l’événement. Tous les messages de l’enregistreur d’événements sont écrits dans la fenêtre de console.


void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
{
    Console.WriteLine("Project Started: " + e.ProjectFile);			
}

void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
{
    Console.WriteLine("Project Finished: " + e.ProjectFile);
}

Répondre aux valeurs de détails de l’enregistreur d’événements

Dans certains cas, vous souhaiterez enregistrer les informations d’un événement uniquement si le commutateur -verbosity de MSBuild.exe contient une certaine valeur. Dans cet exemple, le gestionnaire d’événements TargetStarted n’enregistre un message que si la propriété Verbosity, définie par le commutateur -verbosity, est égale à LoggerVerbosityDetailed.

void eventSource_TargetStarted(object sender, TargetStartedEventArgs e)
{
    if (Verbosity == LoggerVerbosity.Detailed)
    {
        Console.WriteLine("Target Started: " + e.TargetName);
    }
}

Spécifier un enregistreur d’événements

Une fois l’enregistreur d’événements compilé dans un assembly, vous devez indiquer à MSBuild qu’il faut utiliser cet enregistreur d’événements pendant les builds. Pour cela, vous devez utiliser le commutateur -logger avec MSBuild.exe. Pour plus d’informations sur les commutateurs disponibles pour MSBuild.exe, consultez Informations de référence sur la ligne de commande.

La ligne de commande suivante génère le projet MyProject.csproj et utilise la classe d’enregistreur d’événements implémentée dans SimpleLogger.dll. Le commutateur -nologo masque la bannière et le message de copyright, et le commutateur -noconsolelogger désactive l’enregistreur d’événements de console MSBuild par défaut.

MSBuild -nologo -noconsolelogger -logger:SimpleLogger.dll

La ligne de commande suivante génère le projet avec le même enregistreur d’événements, mais avec un niveau Verbosity égal à Detailed.

MSBuild -nologo -noconsolelogger -logger:SimpleLogger.dll -verbosity:Detailed

Exemple 1

Description

L’exemple suivant contient le code complet pour l’enregistreur d’événements.

Code

using System;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;

namespace SimpleLogger
{

    public class MySimpleLogger : Logger
    {
        public override void Initialize(Microsoft.Build.Framework.IEventSource eventSource)
        {
            //Register for the ProjectStarted, TargetStarted, and ProjectFinished events
            eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted);
            eventSource.TargetStarted += new TargetStartedEventHandler(eventSource_TargetStarted);
            eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished);
        }

        void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
        {
            Console.WriteLine("Project Started: " + e.ProjectFile);			
        }

        void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
        {
            Console.WriteLine("Project Finished: " + e.ProjectFile);
        }
        void eventSource_TargetStarted(object sender, TargetStartedEventArgs e)
        {
            if (Verbosity == LoggerVerbosity.Detailed)
            {
                Console.WriteLine("Target Started: " + e.TargetName);
            }
        }
    }
}

Exemple 2

Description

L’exemple suivant montre comment implémenter un enregistreur d’événements qui écrit le journal dans un fichier au lieu de l’afficher dans la fenêtre de console.

Code

using System;
using System.IO;
using System.Security;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace MyLoggers
{
    // This logger will derive from the Microsoft.Build.Utilities.Logger class,
    // which provides it with getters and setters for Verbosity and Parameters,
    // and a default empty Shutdown() implementation.
    public class BasicFileLogger : Logger
    {
        /// <summary>
        /// Initialize is guaranteed to be called by MSBuild at the start of the build
        /// before any events are raised.
        /// </summary>
        public override void Initialize(IEventSource eventSource)
        {
            // The name of the log file should be passed as the first item in the
            // "parameters" specification in the /logger switch.  It is required
            // to pass a log file to this logger. Other loggers may have zero or more than 
            // one parameters.
            if (null == Parameters)
            {
                throw new LoggerException("Log file was not set.");
            }
            string[] parameters = Parameters.Split(';');
            
            string logFile = parameters[0];
            if (String.IsNullOrEmpty(logFile))
            {
                throw new LoggerException("Log file was not set.");
            }
            
            if (parameters.Length > 1)
            {
                throw new LoggerException("Too many parameters passed.");
            }
            
            try
            {
                // Open the file
                this.streamWriter = new StreamWriter(logFile);
            }
            catch (Exception ex)
            {
                if
                (
                    ex is UnauthorizedAccessException
                    || ex is ArgumentNullException
                    || ex is PathTooLongException
                    || ex is DirectoryNotFoundException
                    || ex is NotSupportedException
                    || ex is ArgumentException
                    || ex is SecurityException
                    || ex is IOException
                )
                {
                    throw new LoggerException("Failed to create log file: " + ex.Message);
                }
                else
                {
                    // Unexpected failure
                    throw;
                }
            }

            // For brevity, we'll only register for certain event types. Loggers can also
            // register to handle TargetStarted/Finished and other events.
            eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted);
            eventSource.TaskStarted += new TaskStartedEventHandler(eventSource_TaskStarted);
            eventSource.MessageRaised += new BuildMessageEventHandler(eventSource_MessageRaised);
            eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised);
            eventSource.ErrorRaised += new BuildErrorEventHandler(eventSource_ErrorRaised);
            eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished);
        }

        void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
        {
            // BuildErrorEventArgs adds LineNumber, ColumnNumber, File, amongst other parameters
            string line = String.Format(": ERROR {0}({1},{2}): ", e.File, e.LineNumber, e.ColumnNumber);
            WriteLineWithSenderAndMessage(line, e);
        }
        
        void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
        {
            // BuildWarningEventArgs adds LineNumber, ColumnNumber, File, amongst other parameters
            string line = String.Format(": Warning {0}({1},{2}): ", e.File, e.LineNumber, e.ColumnNumber);
            WriteLineWithSenderAndMessage(line, e);
        }

        void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
        {
            // BuildMessageEventArgs adds Importance to BuildEventArgs
            // Let's take account of the verbosity setting we've been passed in deciding whether to log the message
            if ((e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal))
                || (e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal))
                || (e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))				
                )
            {
                WriteLineWithSenderAndMessage(String.Empty, e);
            }
        }

        void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
        {
            // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
            // To keep this log clean, this logger will ignore these events.
        }
        
        void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
        {
            // ProjectStartedEventArgs adds ProjectFile, TargetNames
            // Just the regular message string is good enough here, so just display that.
            WriteLine(String.Empty, e);
            indent++;
        }

        void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
        {
            // The regular message string is good enough here too.
            indent--;
            WriteLine(String.Empty, e);
        }
        
        /// <summary>
        /// Write a line to the log, adding the SenderName and Message
        /// (these parameters are on all MSBuild event argument objects)
        /// </summary>
        private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
        {
            if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/))
            {
                // Well, if the sender name is MSBuild, let's leave it out for prettiness
                WriteLine(line, e);
            }
            else
            {
                WriteLine(e.SenderName + ": " + line, e);
            }
        }
        
        /// <summary>
        /// Just write a line to the log
        /// </summary>
        private void WriteLine(string line, BuildEventArgs e)
        {
            for (int i = indent; i > 0; i--)
            {
                streamWriter.Write("\t");
            }
            streamWriter.WriteLine(line + e.Message);
        }
        
        /// <summary>
        /// Shutdown() is guaranteed to be called by MSBuild at the end of the build, after all 
        /// events have been raised.
        /// </summary>
        public override void Shutdown()
        {
            // Done logging, let go of the file
            streamWriter.Close();
        }

        private StreamWriter streamWriter;
        private int indent;
    }
}