In one of my [previous posts][1] where I made an HTMLLayout for Log4Net Wim ten Brink commented that I should use XML with xsl instead.

AS I always listen to my followers. I did so swiftly. Here is the XslLayout class I created, Yes I know that there is an XMLLayout class available but that isn’t as good as the one I have.

Imports log4net.Layout
Imports System.Text

Namespace Logging.FileLog
	Public Class XslLayout
		Inherits LayoutSkeleton
		
		Public Overrides Sub ActivateOptions()

		End Sub

		Public Overrides Sub Format(ByVal writer As System.IO.TextWriter, ByVal loggingEvent As log4net.Core.LoggingEvent)
			If loggingEvent Is Nothing Then
				Throw New ArgumentNullException("loggingEvent")
			End If
			writer.Write("<log>")
			writer.Write("<datetime>" & DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") & "</datetime>")
			writer.Write("<thread>" & loggingEvent.ThreadName & "</thread>")
			writer.Write("<level>" & loggingEvent.Level.DisplayName & "</level>")
			If loggingEvent.ExceptionObject IsNot Nothing Then
				writer.Write("<class>" & loggingEvent.ExceptionObject.Source & "</class>")
			Else
				writer.Write("<class>?</class>")
			End If
			writer.Write("<message>")
			loggingEvent.WriteRenderedMessage(writer)
			writer.Write("</message>")
			writer.Write("</log>")
			writer.WriteLine()
		End Sub

		Public Overrides Property Header As String
			Get
				Dim _Header As New StringBuilder
				_Header.Append("<?xml version=""1.0"" encoding=""utf-8""?>")
				_Header.Append("<?xml-stylesheet href=""log.xsl"" type=""text/xsl""?>")
				_Header.Append("<logs>")
				_Header.Append("<header>Started logging at " & DateTime.Now & "</header>")
				Return _Header.ToString
			End Get
			Set(ByVal value As String)
				MyBase.Header = value
			End Set
		End Property

		Public Overrides Property Footer As String
			Get
				Dim _Footer As New StringBuilder
				_Footer.Append("<footer>Stopped logging at " & DateTime.Now & "</footer>")
				_Footer.Append("</logs>")
				Return _Footer.ToString
			End Get
			Set(ByVal value As String)
				MyBase.Footer = value
			End Set
		End Property
	End Class
End Namespace```
and this is the xsl file to go with it.

```xml
<?xml version="1.0"?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <title>LogFile</title>
        <style type="text/css">
          table, th, td{ border: 1px solid black;}
          table {border-collapse:collapse;width:100%;}
          th{background-color:gray; color:white;}
          .warn{color:red;}
          .info{color:green;}
          .alternatecolor{background-color:lightCyan;}
        </style>
      </head>
      <body>
          <p>
            <xsl:value-of select="logs/header"/>
          </p>
          <table>
            <tr>
              <th>Date time</th>
              <th>Thread</th>
              <th>Level</th>
              <th>Class</th>
              <th>Message</th>
            </tr>
            <xsl:for-each select="logs/log">
              <tr>
                <xsl:if test="position() mod 2 = 1">
                  <xsl:attribute name="class">
                    <xsl:value-of select="translate(level, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ', 'abcdefghijklmnopqrstuvwxyz_')"/>
                  </xsl:attribute>
                </xsl:if>
                <xsl:if test="position() mod 2 = 0">
                  <xsl:attribute name="class">
                    <xsl:value-of select="translate(level, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ', 'abcdefghijklmnopqrstuvwxyz_')"/>
                    alternatecolor
                  </xsl:attribute>
                </xsl:if>
                <td>
                  <xsl:value-of select="datetime"/>
                </td>
                <td>
                  <xsl:value-of select="level"/>
                </td>
                <td>
                  <xsl:value-of select="thread"/>
                </td>
                <td>
                  <xsl:value-of select="class"/>
                </td>
                <td>
                  <xsl:value-of select="message"/>
                </td>
              </tr>
            </xsl:for-each>
          </table>
          <p>
            <xsl:value-of select="logs/footer"/>
          </p>
        <hr />
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>```
That was fun.

And here the result.

The XML logfile.

```xml
<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href="log.xsl" type="text/xsl"?><logs><header>Started logging at 9/02/2011 14:27:11</header><log><datetime>2011-02-09 14:27:16</datetime><thread>10</thread><level>INFO</level><class>?</class><message>Using NHibernateConfiguration. Database:  Server: </message></log>
<log><datetime>2011-02-09 14:27:18</datetime><thread>10</thread><level>DEBUG</level><class>?</class><message>Constructor - Log initialized</message></log>
<log><datetime>2011-02-09 14:27:19</datetime><thread>10</thread><level>WARN</level><class>?</class><message>Is this warning on?</message></log>
<log><datetime>2011-02-09 14:27:19</datetime><thread>10</thread><level>DEBUG</level><class>?</class><message>Is this debug on?</message></log>
<log><datetime>2011-02-09 14:27:19</datetime><thread>10</thread><level>INFO</level><class>?</class><message>Is this info on?</message></log>
<footer>Stopped logging at 9/02/2011 14:27:22</footer></logs>```
And the result in the browser.

<div class="image_block">
  <a href="https://lessthandot.z19.web.core.windows.net/wp-content/uploads/users/chrissie1/log4net/log4net2.png?mtime=1297245330"><img alt="" src="https://lessthandot.z19.web.core.windows.net/wp-content/uploads/users/chrissie1/log4net/log4net2.png?mtime=1297245330" width="1006" height="287" /></a>
</div>

That looks exactly the same as previous and the resulting logfile is machinereadable to boot.

<span class="MT_red">Warning: Your appender should have the AppendToFile attribute set to False else this will generate invalid XML. That is also the biggest drawback of this method. You could of course just create invalid XML and then write a parser but that was not really the point of the exercise.</p> 

<p>
  Warnign 2: You also want to write the xsl to where the log file has been written else the result will be less satisfying.</span>
</p>

 [1]: /index.php/DesktopDev/MSTech/why-do-you-make-unreadable