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