Chaitanya's Blog

Adding a custom msbuild logger to TeamBuild

This blog explains how to add a custom logger to the teambuild process in the Beta3 version (which will be releasing shortly)  of TeamFoundation.  The TeamBuild logger currently logs certain events such as solutions/projects built, their status, completion times etc. to a database and displays this in the build report. This information is derived from events raised by msbuild. In teambuild all events raised by msbuild are not stored/shown in the build report. If a user wants to capture these other events raised by msbuild during the build process (say information about tasks that are executed), then she can write her own custom logger and  write them to a different file and then write her own custom report that displays this information.

Let us say we want to log all events raised by msbuild in an xml format. We can do this in the following way - We can add each event recieved as an xml element with the most basic information such as timestamp, status, message etc. Each event type raised by msbuild (eg Build, Project, Message etc.) can be used as an xml tagname to describe that element type. The properties of the event that we are interested can be logged as  attributes of that element.

The following is the code for such a simple logger
(note: this code is only for illustration purpose and doesn't have adequate exception handling)

using System;
using Microsoft.Build.Framework;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Text;

public class XMLLogger : ILogger
{
  
    private XmlTextWriter w;

    public void Initialize(IEventSource source)
    {
        source.BuildFinished += new BuildFinishedEventHandler(BuildFinishHandler);
 source.BuildStarted += new BuildStartedEventHandler(BuildStartHandler);

        source.ProjectStarted += new ProjectStartedEventHandler(ProjectStartHandler);
        source.ProjectFinished += new ProjectFinishedEventHandler(ProjectFinishHandler);

        source.TargetStarted += new TargetStartedEventHandler(TargetStartedHandler);
        source.TargetFinished += new TargetFinishedEventHandler(TargetFinishedHandler);;

 source.TaskStarted += new TaskStartedEventHandler(TaskStartedHandler);
 source.TaskFinished += new TaskFinishedEventHandler(TaskFinishedHandler);
 source.ErrorRaised += new BuildErrorEventHandler(ErrorHandler);
 source.WarningRaised += new BuildWarningEventHandler(WarningHandler);
 source.MessageRaised += new BuildMessageEventHandler(MessageHandler);

 w = new XmlTextWriter("BuildLog.xml", Encoding.ASCII);
 w.WriteStartDocument();
        w.WriteStartElement("BuildLog");
    }

 void BuildStartHandler(Object sender, BuildStartedEventArgs arg)
 {
        w.WriteStartElement("BuildStarted");
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteEndElement();
 }

 void BuildFinishHandler(Object sender, BuildFinishedEventArgs arg)
 {
        w.WriteStartElement("BuildFinished");
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteAttributeString("Status", arg.Succeeded.ToString());
  w.WriteEndElement();
 }

 void ProjectStartHandler(Object sender, ProjectStartedEventArgs arg)
 {
        w.WriteStartElement("ProjectStarted");
        w.WriteAttributeString("Name", Path.GetFileName(arg.ProjectFile));
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteEndElement();
 }

 void ProjectFinishHandler(Object sender, ProjectFinishedEventArgs arg)
 {
        w.WriteStartElement("ProjectFinished");
        w.WriteAttributeString("Name", Path.GetFileName(arg.ProjectFile));
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteAttributeString("Status", arg.Succeeded.ToString());
        w.WriteEndElement();
    }

 void TargetStartedHandler(Object sender, TargetStartedEventArgs arg)
 {
  w.WriteStartElement("TargetStarted");
        w.WriteAttributeString("Name", arg.TargetName);
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteEndElement();
    }
 void TargetFinishedHandler(Object sender, TargetFinishedEventArgs arg)
 {
        w.WriteStartElement("TargetFinished");
        w.WriteAttributeString("Name", arg.TargetName);
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteAttributeString("Status", arg.Succeeded.ToString());
        w.WriteEndElement();
 }

 void TaskStartedHandler(Object sender, TaskStartedEventArgs arg)
 {
        w.WriteStartElement("TaskStarted");
        w.WriteAttributeString("Name", arg.TaskName);
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteEndElement();
    }
 void TaskFinishedHandler(Object sender, TaskFinishedEventArgs arg)
 {
        w.WriteStartElement("TaskFinished");
        w.WriteAttributeString("Name", arg.TaskName);
        w.WriteAttributeString("Time", arg.Timestamp.ToString());
        w.WriteAttributeString("Status", arg.Succeeded.ToString());
        w.WriteEndElement();
    }

 void ErrorHandler(Object sender, BuildErrorEventArgs arg)
 {
  w.WriteStartElement("Error");
  w.WriteAttributeString("Message", arg.Message);
  w.WriteEndElement();
 }
 void WarningHandler(Object sender, BuildWarningEventArgs arg)
 {
  w.WriteStartElement("Warning");
  w.WriteAttributeString("Message", arg.Message);
  w.WriteEndElement();
 }

 void MessageHandler(Object sender, BuildMessageEventArgs arg)
 {
  w.WriteStartElement("Message");
  w.WriteAttributeString("Message", arg.Message);
  w.WriteEndElement();
 }

    public void Shutdown()
    {
        if (w != null)
        {
            w.WriteEndElement();
            w.Close();
        }
    }

    private LoggerVerbosity verbosity;
   
    public LoggerVerbosity Verbosity
    {
        get
        {
            return verbosity;
        }

        set
        {
            verbosity = value;
        }
    }

    private String parameters;

    public String Parameters
    {
        get
        {
            return parameters;
        }

        set
        {
            parameters = value;
        }
    }

}


After building this  logger as a library(say xmllogger.dll) we need to include it in the teambuild process. This can be done using the  TfsBuild.rsp file. Additional Msbuild options can be passed via teambuild by including them in this file. (Check out http://blogs.msdn.com/manishagarwal/default.aspx for more uses of the .rsp file). This file is located at the following source control folder: $<Your Team Project>\TeamBuildTypes\<Your BuildTypeName>. We edit this file
 and add the following line to this file

/l:XMLLogger,xmllogger.dll

We checkin this file and also the xmllogger.dll to the same directory. After we do this msbuild will use this logger and log events in xml format to the "BuildLog.xml" file.


After this we need to do one more thing which is to drop this BuildLog.xml file to the drop location along with the rest of the log files and binaries. This can be done by overriding the "AfterDropBuild" Target in the Microsoft.TeamFoundation.Build.Targets file (located at %Programfiles%\Msbuild\Microsoft\VisualStudio\v8.0\TeamBuild). (Again manish has a blog on how to do this at http://blogs.msdn.com/manishagarwal/default.aspx. ) This target can be overriden in the TfsBuild.proj file which is also  located in same folder as the TfsBuild.rsp file. We edit the TfsBuild.Proj file and call the copy task in this target as shown below.

  <Target Name="AfterDropBuild">

    <Copy
          SourceFiles="$(MSBuildProjectDirectory)\BuildLog.xml"
          DestinationFolder="$(DropLocation)\$(BuildNumber)"
          ContinueOnError="true" />

  </Target>


After all the binaries are dropped to the dropsite,  this step will drop the Buildlog.xml to the droplocation.

 

 

 

Published Tuesday, September 13, 2005 11:17 AM by chaitanyacheruvu

Comments

 

Team System News said:

Rob Caron - Joel Semeniuk on Team System (.NET Rocks!)

Joel does a great job in this episode of .NET...
September 14, 2005 1:16 AM
 

Gautam Goenka (MSFT) said:

While building using Team Build, the build process creates one log file (for&amp;nbsp;compilation and code...
April 19, 2006 9:30 AM
 

Aaron Hallberg said:

Most of the magic in a Team Build is done using either (a) customized tasks, or (b) a customized logger.&amp;nbsp;&amp;nbsp;Well,...
August 30, 2006 9:02 AM
 

Aaron Hallberg said:

Most of the magic in a Team Build is done using either (a) customized tasks, or (b) a customized logger

April 4, 2007 3:09 PM
Anonymous comments are disabled

This Blog

Syndication

Tags

No tags have been created or used yet.

News

All postings in this blog is provided "AS IS" with no warranties, and confers no rights. The statements and opinions expressed herein are my own and are not made on behalf of my employer.

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker