Создание средства ведения журнала сборки
Средства ведения журнала позволяют настраивать выходные данные сборки и отображать сообщения, ошибки или предупреждения в ответ на определенные события сборки. Каждое средство ведения журнала существует в виде класса .NET, который реализует интерфейс ILogger, определенный в сборке Microsoft.Build.Framework.dll.
Для средства ведения журнала можно использовать два подхода к реализации:
Прямая реализация интерфейса ILogger.
Произведите класс от вспомогательного класса Logger, который определен в сборке Microsoft.Build.Utilities.dll. Logger реализует ILogger и предоставляет реализацию по умолчанию некоторых элементов ILogger.
В этой статье мы расскажем, как создать простое средство ведения журнала, унаследованное от Logger, которое отображает в консоли сообщения в ответ на определенные события сборки.
Регистрация для событий
Средство ведения журнала предназначено для сбора сведений о выполнении сборки, которые поступает от обработчика сборки, и для представления этой информации в полезном формате. Все средства ведения журнала должны переопределять метод Initialize, в котором средство ведения журнала регистрируется для событий. В этом примере средство ведения журнала регистрируется для событий TargetStarted, ProjectStarted и 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);
}
Реагирование на события
Теперь, когда средство ведения журнала зарегистрировано для определенных событий, оно будет обрабатывать эти события при их возникновении. Для событий ProjectStarted и 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);
}
Реагирование на значения детализации для средства ведения журнала
В некоторых случаях информацию о событии нужно записывать, только если параметр -verbosity для MSBuild.exe содержит определенное значение. В этом примере обработчик событий TargetStarted записывает сообщение, только если свойство Verbosity, которое задается параметром -verbosity, равно LoggerVerbosityDetailed
.
void eventSource_TargetStarted(object sender, TargetStartedEventArgs e)
{
if (Verbosity == LoggerVerbosity.Detailed)
{
Console.WriteLine("Target Started: " + e.TargetName);
}
}
Указание средства ведения журнала
Когда средство ведения журнала скомпилировано в сборку, нужно передать в MSBuild информацию о том, что это средство ведения журнала следует использовать во время сборки. Для этого используйте параметр -logger в MSBuild.exe. Дополнительные сведения о доступных параметрах MSBuild.exe см. в справочнике по командной строке.
Следующая команда выполняет сборку проекта MyProject.csproj с использованием класса ведения журнала, который реализован в SimpleLogger.dll. Параметр -nologo позволяет скрыть баннер и сообщение об авторских правах, а параметр -noconsolelogger отключает используемое по умолчанию консольное средство ведения журнала MSBuild.
MSBuild -nologo -noconsolelogger -logger:SimpleLogger.dll
Следующая командная строка выполняет сборку проекта с тем же средством ведения журнала, но уже с уровнем Verbosity
для Detailed
.
MSBuild -nologo -noconsolelogger -logger:SimpleLogger.dll -verbosity:Detailed
Пример 1
Description
В следующем примере приведен полный код средства ведения журнала.
Код
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);
}
}
}
}
Пример 2
Description
В следующем примере показано, как реализовать средство ведения журнала, которое записывает журнал в файл, а не окно консоли.
Код
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;
}
}