Why do you make unreadable logfiles? I was talking to myself there, me and myself tend to have such conversations a lot lately. Anyway, we digress.
I used to have some simple output for my logfiles.
[Header started logging at 3/02/2011 13:20:38]
2011-02-03 13:20:48,003 [1] WARN Logger [TDB2009.View.Menu.Winforms.Startup.StartUp] - Is this warning on?
[footer]
If you put that in debugging mode you will end up with a lot of files and not much of it will be readable. Finding the warns will be difficult at best.
I can do two things to improve on that.
Write a parser and make it pretty.
Download a parser that does it for me.
Make it HTML.
I choose to make it HTML. Not the perfect solution, but OK for now.
I used a coded solution since I don’t think these things need to change at runtime anyway, it’s a client application, so the only thing that really needs to change at runtime might be the loglevel.
Layout.Header = "<!DOCTYPE html>"
Layout.Header &= "<html lang=""en"">"
Layout.Header &= "<head>"
Layout.Header &= "<meta charset=""utf-8"" />"
Layout.Header &= "<title>LogFile</title>"
Layout.Header &= "<style type=""text/css"">"
Layout.Header &= "table{border-collapse:collapse;width:100%; }"
Layout.Header &= "table, th, td{ border: 1px solid black;}"
Layout.Header &= "th{background-color:gray; color:white;}"
Layout.Header &= ".WARN{color:red;}"
Layout.Header &= ".DEBUG{color:green;}"
Layout.Header &= "</style>"
Layout.Header &= "</head>"
Layout.Header &= "<body>"
Layout.Header &= "<p>Started logging at " & DateTime.Now & "</p>"
Layout.Header &= "<table>"
Layout.Header &= "<tr><th style=""width:200px;"">Date and time</th><th style=""width:100px;"">Thread</th><th style=""width:100px;"">Level</th><th style=""width:100px;"">Logger</th><th style=""width:200px;"">Class</th><th>Message</th></tr>"
Layout.ConversionPattern = "<tr class=""%-5level""><td>%date</td><td>%thread</td><td>%-5level</td><td>%logger</td><td> %class </td><td>%message</td></tr>"
Layout.Footer = "</table>"
Layout.Footer &= "<p>Stopped logging</p>"
Layout.Footer &= "<hr />"
Layout.Footer &= "</body></html>"```
With this being the result.
<div class="image_block">
<a href="/wp-content/uploads/users/chrissie1/log4net/log4net1.png?mtime=1297180388"><img alt="" src="/wp-content/uploads/users/chrissie1/log4net/log4net1.png?mtime=1297180388" width="1001" height="418" /></a>
</div>
I will need to check what the result will be if the file is split up over multiple files since it won’t see the css anymore. It won’t slow things down too much since the conversion pattern is only slightly bigger, I might want to make info green and debug black too.
The above worksjust fine, but fine isn’t good enough, so I decide to do better and create my own LayoutClass.
Making your own LayoutClass is easy. You just have to inherit LayoutSkeleton and implement some methods ;-).
And here it is.
```vbnet
Imports log4net.Layout
Imports System.Text
Namespace Logging.FileLog
Public Class HtmlLayout
Inherits LayoutSkeleton
Private Shared AlternateColor As Boolean = True
Public Sub New()
IgnoresException = True
End Sub
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
If AlternateColor Then
writer.Write("<tr class=""" & loggingEvent.Level.ToString.ToLower & """>")
Else
writer.Write("<tr class=""" & loggingEvent.Level.ToString.ToLower & " alternatecolor"">")
End If
AlternateColor = Not AlternateColor
writer.Write("<td>" & DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") & "</td>")
writer.Write("<td>" & loggingEvent.ThreadName & "</td>")
writer.Write("<td>" & loggingEvent.Level.DisplayName & "</td>")
If loggingEvent.ExceptionObject IsNot Nothing Then
writer.Write("<td>" & loggingEvent.ExceptionObject.Source & "</td>")
Else
writer.Write("<td>?</td>")
End If
writer.Write("<td>")
loggingEvent.WriteRenderedMessage(writer)
writer.Write("</td></tr>")
writer.WriteLine()
End Sub
Public Overrides Property Header As String
Get
Dim _Header As New StringBuilder
_Header.Append("<!DOCTYPE html>")
_Header.Append("<html lang=""en"">")
_Header.Append("<head>")
_Header.Append("<meta charset=""utf-8"" />")
_Header.Append("<title>LogFile</title>")
_Header.Append("<style type=""text/css"">")
_Header.Append("table, th, td{ border: 1px solid black;}")
_Header.Append("th{background-color:gray; color:white;}")
_Header.Append(".warn{color:red;}")
_Header.Append(".info{color:green;}")
_Header.Append(".alternatecolor{background-color:lightCyan;}")
_Header.Append("</style>")
_Header.Append("</head>")
_Header.Append("<body>")
_Header.Append("<p>Started logging at " & DateTime.Now & "</p>")
_Header.Append("<table style=""border-collapse:collapse;width:100%;border: 1px solid black;"">")
_Header.Append("<tr><th style=""width:200px;"">Date and time</th><th style=""width:100px;"">Thread</th><th style=""width:100px;"">Level</th><th style=""width:200px;"">Class</th><th>Message</th></tr>")
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("</table>")
_Footer.Append("<p>Stopped logging at " & DateTime.Now & "</p>")
_Footer.Append("<hr />")
_Footer.Append("</body></html>")
Return _Footer.ToString
End Get
Set(ByVal value As String)
MyBase.Footer = value
End Set
End Property
End Class
End Namespace```
Oh my I used a static variable, but no harm was done.
And here is the result.
<div class="image_block">
<a href="/wp-content/uploads/users/chrissie1/log4net/log4net2.png?mtime=1297245330"><img alt="" src="/wp-content/uploads/users/chrissie1/log4net/log4net2.png?mtime=1297245330" width="1006" height="287" /></a>
</div>
Kinda much more readable. And very easy to implement.
When I now create an appender I just have to give it this layout.
```vbnet
Dim _RollingFileAppender = new RollingFileAppender()
' Do the config jig
_RollingFileAppender.Layout = new HTMLLayout()
_RollingFileAppender.ActivateOptions()
When it starts a new file it will print the footer in the last file and print a header in the new file.
Did you notice how I now have the datetime in the footer. If you do this with the patternlayout it will not do this correctly because the footerproperty is set at config time not at runtime.