Bagikan melalui


Membuat pencatat build

Pencatat menyediakan cara bagi Anda untuk menyesuaikan output build dan menampilkan pesan, kesalahan, atau peringatan sebagai respons terhadap peristiwa build tertentu. Setiap pencatat diimplementasikan sebagai kelas .NET yang mengimplementasikan ILogger antarmuka, yang didefinisikan dalam rakitan Microsoft.Build.Framework.dll.

Ada dua pendekatan yang dapat Anda gunakan saat menerapkan pencatat:

  • Terapkan antarmuka secara ILogger langsung.

  • Dapatkan kelas Anda dari kelas pembantu, Logger, yang didefinisikan dalam perakitan Microsoft.Build.Utilities.dll. Logger mengimplementasikan ILogger dan menyediakan implementasi default dari beberapa anggota ILogger.

    Topik ini akan menjelaskan cara menulis pencatat sederhana yang berasal dari Logger, dan menampilkan pesan di konsol sebagai respons terhadap peristiwa build tertentu.

Daftar untuk peristiwa

Tujuan pencatat adalah untuk mengumpulkan informasi tentang kemajuan build seperti yang dilaporkan oleh mesin build, dan kemudian melaporkan informasi tersebut dengan cara yang berguna. Semua pencatat harus mengambil alih Initialize metode, yang merupakan tempat pencatat mendaftar untuk peristiwa. Dalam contoh ini, pencatat mendaftar untuk peristiwa TargetStarted, ProjectStarted, dan 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);
    }

Merespons aktivitas

Sekarang setelah pencatat terdaftar untuk peristiwa tertentu, pencatat perlu menangani peristiwa tersebut ketika terjadi. Untuk peristiwa ProjectStarted dan ProjectFinished, pencatat hanya menulis frasa pendek dan nama file proyek yang terlibat dalam peristiwa tersebut. Semua pesan dari pencatat ditulis ke jendela konsol.


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);
}

Merespons nilai verbositas pencatat

Dalam beberapa kasus, Anda mungkin hanya ingin mencatat informasi dari suatu peristiwa jika sakelar MSBuild.exe -verbosity berisi nilai tertentu. Dalam contoh ini, penanganan TargetStarted aktivitas hanya mencatat pesan jika Verbosity properti, yang diatur oleh sakelar -verbosity, sama dengan LoggerVerbosityDetailed.

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

Tentukan pencatat

Setelah pencatat dikompilasi ke dalam perakitan, Anda perlu memberi tahu MSBuild untuk menggunakan pencatat tersebut selama build. Ini dilakukan menggunakan sakelar -pencatat dengan MSBuild.exe. Untuk informasi selengkapnya tentang sakelar yang tersedia untuk MSBuild.exe, lihat Referensi baris perintah.

Baris perintah berikut membangun proyek MyProject.csproj dan menggunakan kelas pencatat yang diterapkan di SimpleLogger.dll. Sakelar -nologo menyembunyikan spanduk dan pesan hak cipta dan sakelar -noconsolelogger menonaktifkan pencatat konsol MSBuild default.

MSBuild -nologo -noconsolelogger -logger:SimpleLogger.dll

Baris perintah berikut membangun proyek dengan pencatat yang sama, tetapi dengan Verbosity tingkat Detailed.

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

Contoh 1

Deskripsi

Contoh berikut berisi kode lengkap untuk pencatat.

Kode

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);
            }
        }
    }
}

Contoh 2

Deskripsi

Contoh berikut menunjukkan cara mengimplementasikan pencatat yang menulis log ke file daripada menampilkannya di jendela konsol.

Kode

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;
    }
}