빌드 로거
로거를 사용하여 빌드 출력을 사용자 지정하고 특정 빌드 이벤트에 대한 응답으로 메시지, 오류 또는 경고를 표시할 수 있습니다. 각 로거는 Microsoft.Build.Framework.dll 어셈블리에 정의되어 있는 ILogger 인터페이스를 구현하는 .NET 클래스로 구현됩니다.
다음 두 가지 방법을 사용하여 로거를 구현할 수 있습니다.
ILogger 인터페이스를 직접 구현합니다.
Microsoft.Build.Utilities.dll 어셈블리에 정의되어 있는 Logger 도우미 클래스에서 클래스를 파생시킵니다. 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);
}
로거의 자세한 정도 값에 대한 응답
MSBuild.exe /verbosity 스위치에 특정 값이 있더라도 이벤트의 정보만 기록하려는 경우가 있습니다. 이 예제에서 TargetStarted 이벤트 처리기는 /verbosity 스위치로 설정되는 Verbosity 속성이 LoggerVerbosity Detailed인 경우에만 메시지를 기록합니다.
void eventSource_TargetStarted(object sender, TargetStartedEventArgs e)
{
if (Verbosity == LoggerVerbosity.Detailed)
{
Console.WriteLine("Target Started: " + e.TargetName);
}
}
로거 지정
로거가 어셈블리로 컴파일되면 빌드하는 동안 해당 로거를 사용하도록 MSBuild에 지시해야 합니다. 이 작업은 MSBuild.exe에 /logger 스위치를 사용하여 수행됩니다. MSBuild.exe에 사용할 수 있는 스위치에 대한 자세한 내용은 MSBuild 명령줄 참조를 참조하십시오.
다음 명령줄에서는 MyProject.csproj 프로젝트를 빌드하고 SimpleLogger.dll에 구현된 로거 클래스를 사용합니다. /nologo 스위치는 배너와 저작권 메시지를 숨기고 /noconsolelogger 스위치는 기본 MSBuild 콘솔 로거를 비활성화합니다.
MSBuild /nologo /noconsolelogger /logger:SimpleLogger.dll
다음 명령줄에서는 같은 로거를 사용하지만 Verbosity 수준을 Detailed로 지정하여 프로젝트를 빌드합니다.
MSBuild /nologo /noconsolelogger /logger:SimpleLogger.dll /verbosity:Detailed
예제
설명
다음 예제에는 로거에 대한 전체 코드가 있습니다.
코드
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);
}
}
}
}
예제
설명
다음 예제에서는 로그를 콘솔 창에 표시하는 대신 파일에 쓰는 로거를 구현하는 방법을 보여 줍니다.
코드
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;
}
}