MSDeploy , XPath and XSL: Part II: Generating formatted output

Using xsl and msxsl with msdeploy you can generate formatted html logs. It gets even better when you use the API; you can generate better organized logs with custom messages to suit your needs. Before we start, you can download msxsl for free from here and the web deployment tool (msdeploy) from here (x86 / x64 )

Using the MSDeploy.exe:

I have a simple xsl that shows the files or directories or anything that is either added, deleted, modified or skipped in a table format. The xml spew that is generated by specifying –xml switch to msdeploy can be piped into msxsl along with the attached xsl file and then this can be opened in internet explorer or a browser of your choice all in one command. The xsl can be modified to output non xml output. For this you can look at the part III of the post.

The following command creates a package of the webserver while skipping all asp files in the server and pipes the xml output to msxsl. Msxsl uses the piped xml and applies the msdeploy.xsl to it to generate out.html which is opened by the browser.

C:\>msdeploy -verb:sync -source:webserver -dest:package=e:\pack.zip -xml -skip:objectName=filePath;absolutePath=\.asp -verbose | f:\xsl\msxsl – f:\xsl\msdeploy.xsl -o out.html & iexplore c:\out.html

You can download the xsl file from here. (Its currently named msdeploy.xsl.txt since uploading xsl files is not allowed. Make sure to rename it to xsl before using it)

Using the MSDeploy API:

An xsl similar to the one above is used. For getting formatted output from the API you need to do the following: You can download the the visual studio solution for this from here

1. Add traceeventhandler to the source and / or destination base options. Tracelevel needs to be verbose to get skipped object and replace rule traces. The default tracelevel is info which gives just the DeploymentObjectChangedEventArgs.

DeploymentBaseOptions baseOptions = new DeploymentBaseOptions();
baseOptions.Trace += TraceEventHandler;
baseOptions.TraceLevel = TraceLevel.Verbose;

2. Implement the traceeventhandler function

You can access the provider name, path and the operationtype easily in DeploymentObjectChangedEventArgs  but for other traceEvents you need to use reflection to access these values as shown below.

private void TraceEventHandler(object sender, DeploymentTraceEventArgs e)
        {
            DeploymentObjectChangedEventArgs ChangedEvent = e as DeploymentObjectChangedEventArgs;
            if (ChangedEvent != null)
            {
                WriteEventEntry(ChangedEvent.ProviderName, ChangedEvent.Path, ChangedEvent.OperationType.ToString(), "");
            }
            else
            {
                DeploymentTraceEventArgs TraceEvent = e as DeploymentTraceEventArgs;
                if (TraceEvent != null)
                {
                    string strMsg = TraceEvent.Message;
                    string strProviderName = "", strPath = "", strRuleName = "";
                    if (strMsg.ToLower().Contains("skip"))
                    {
                        //DeploymentRuleSkipObjectEvent has _object as the deploymentObject
                        FieldInfo fi = TraceEvent.GetType().GetField("_object", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                        if (fi == null)
                        {
                            //DeploymentRuleSkipEvent has _destObject as the deploymentObject
                            fi = TraceEvent.GetType().GetField("_destObject", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                        }

                        if (fi != null)
                        {
                            strPath = ((DeploymentObject)fi.GetValue(TraceEvent)).AbsolutePath;
                            strProviderName = ((DeploymentObject)fi.GetValue(TraceEvent)).Name;
                            //Get the _rule object and get its name
                            fi = TraceEvent.GetType().GetField("_rule", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                            strRuleName = fi != null ? ((DeploymentRule)fi.GetValue(TraceEvent)).Name : "";
                        }
                        else
                        {
                            //We are here implies its a DeploymentSkipDirective
                            fi = TraceEvent.GetType().GetField("_directiveName", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                            strRuleName = fi != null ? fi.GetValue(TraceEvent).ToString() : "";
                            fi = TraceEvent.GetType().GetField("_objectName", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                            strProviderName = fi != null ? fi.GetValue(TraceEvent).ToString() : "";
                            fi = TraceEvent.GetType().GetField("_absolutePath", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                            strPath = fi != null ? fi.GetValue(TraceEvent).ToString() : "";
                        }
                        WriteEventEntry(strProviderName, strPath, "Skipped", strRuleName);
                    }
                }
            }
        }

3. Implement the writeevententry function to write xml output

private void WriteEventEntry(string strProvName, string strPath, string strOperationType, string strRuleName)
        {           
            m_xmlOut.WriteStartElement("object");

            m_xmlOut.WriteStartElement("name");
            m_xmlOut.WriteValue(strProvName);
            m_xmlOut.WriteEndElement();

            m_xmlOut.WriteStartElement("path");
            m_xmlOut.WriteValue(strPath);
            m_xmlOut.WriteEndElement();

            m_xmlOut.WriteStartElement("operationType");
            m_xmlOut.WriteValue(strOperationType);
            m_xmlOut.WriteEndElement();

            m_xmlOut.WriteStartElement("Rule");
            m_xmlOut.WriteValue(strRuleName);
            m_xmlOut.WriteEndElement();

            m_xmlOut.WriteEndElement();
        }             

4. Call the generatehtml with the xsl provided to get transformed html

XPathDocument xPathDoc = new XPathDocument(m_strXmlLogFilePath);
XslCompiledTransform xslTrans = new XslCompiledTransform();
xslTrans.Load(Environment.CurrentDirectory + @"\TransformOutput.xslt");
XmlTextWriter xW = new XmlTextWriter(m_strLogFileDir + @"\MSDepLog.html", null);
xslTrans.Transform(xPathDoc, null, xW);
xW.Close();

Thats all there is to it. The xsl and the code can be modified to suit your needs such as tracing replace events etc.

1 Comment

Comments have been disabled for this content.