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 fired.
/// </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.
string[] parameters = Parameters.Split(';');

string logFile = parameters0;
if ((logFile == null) || (logFile.Length == 0))
{
throw new ApplicationException("Log file was not set.");
}

if (parameters.Length > 1)
{
throw new ApplicationException("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 ApplicationException("Failed to create log file: " + ex.ToString());
}
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 && VerbosityIsGreaterThanOrEqual(LoggerVerbosity.Minimal))
|| (e.Importance == MessageImportance.Normal && VerbosityIsGreaterThanOrEqual(LoggerVerbosity.Normal))
|| (e.Importance == MessageImportance.Low && VerbosityIsGreaterThanOrEqual(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 fired.
/// </summary>
public override void Shutdown()
{
		 			// Done logging, let go of the file
	
streamWriter.Close();
}

/// <summary>
/// Returns true if the verbosity is more verbose or equal verbosity
/// to the threshold passed in. Otherwise returns false
/// </summary>
private bool VerbosityIsGreaterThanOrEqual(LoggerVerbosity threshold)
{
bool result = false;

switch (threshold)
{
case LoggerVerbosity.Diagnostic:
result = (LoggerVerbosity.Diagnostic == Verbosity);
break;

case LoggerVerbosity.Detailed:
result = ((LoggerVerbosity.Detailed == Verbosity)
|| (LoggerVerbosity.Diagnostic == Verbosity));
break;

case LoggerVerbosity.Normal:
result = ((LoggerVerbosity.Detailed == Verbosity)
|| (LoggerVerbosity.Diagnostic == Verbosity)
|| (LoggerVerbosity.Normal == Verbosity));
break;

case LoggerVerbosity.Minimal:
result = ((LoggerVerbosity.Detailed == Verbosity)
|| (LoggerVerbosity.Diagnostic == Verbosity)
|| (LoggerVerbosity.Normal == Verbosity)
|| (LoggerVerbosity.Minimal == Verbosity));
break;

case LoggerVerbosity.Quiet:
result = true;
break;
}

return result;
}

private StreamWriter streamWriter;
private int indent;
		 	}
		 }
	

Several improvements could be made to improve the code above:

-- VerbosityIsGreaterThanOrEqual could simply use ">=" on the enum value instead of the switch.
-- In Beta 2, there is a special Microsoft.Build.Framework.LoggerException that can be thrown instead of the ApplicationException. It still stops the build, but it does it cleanly, without dumping the stack as if the logger failed.
Microsoft Communities