Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Simple Log Viewer

5.00/5 (4 votes)
21 Feb 2021MIT2 min read 8.7K   213  
Show Simple Log - see the article by Jochen Scharr - in the browser with a nice layout
Simple Log by Jochen Scharr writes log items in XML format. This small extension adds an embedded XSLT stylesheet to the XML. While still unchanged and valid XML browsers pick up the XSLT processing instructions to render the XML in an HTML table.

Introduction

The article, Simple Log by Jochen Scharr, presents a very effective and simple to use logging class. The logger writes items as an XML file which is ideal for analysis and automated processing with XPath. It can show the XML file in a browser or XML editor but that can be daunting for a user to look at. Especially when talking to a user remotely, it hampers communication. This small extension shows the log entries as a table instead of an XML tree in a browser.

If you are not familiar with the Simple Log class, read the article by Jochen Schurr first!

Sample output of Simple Log

Sample output of simple log entries

Background

Although not often used, XML files can have processing instructions that browsers use to transform the XML. These processing instructions are XSLT stylesheets that can be embedded in the XML. The XML file is still valid and can be processed normally but if opened in a browser, the XSL code is executed before rendering the XML.

Simple Log uses a straightforward XML structure where only exceptions are nested. The XSL creates an HTML table for easy reading of the log entries.

The XSL code and CSS style sheet are embedded in the XML file that is saved with the ShowLogFile method.

XML
  1  <?xml version="1.0" encoding="UTF-8"?>
  2  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  3    <xml:comment>XML logger export stylesheet for Chromium and FireFox rendering engines
  4    </xml:comment>
  5    <xsl:output method="html" version="4.0" encoding="ISO-8859-1" indent="no" 
  6     omit-xml-declaration="yes"/>
  7    <xsl:template match="/">
  8    <html>
  9      <head>
 10        <meta NAME="ROBOTS" CONTENT="NOINDEX,NOFOLLOW"/>
 11        <meta HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE"/>
 12        <title>Log <xsl:value-of select="/LogEntries/@Date"/></title>
 13        <style media="screen" type="text/css">
 14   body {
 15   background-color: #F1F0EB;
 16   font-family: Verdana, Tahoma, Helvetica, sans-serif;
 17   font-size: .8em;
 18   margin: 10, 10, 10, 10;
 19   }
 20  
 21   h1 {
 22   font-size: 12px;
 23   color: #A0A0A0;
 24   }
 25  
 26   table {
 27   border-width: 1px;
 28   border-spacing: 2px;
 29   border-style: solid;
 30   border-color: gray;
 31   border-collapse: separate;
 32   background-color: #F1F0EB;
 33   }
 34  
 35   table thead {
 36   border-width: 0px;
 37   font-weight: bold;
 38   line-height: 1.2em;
 39   padding: 1px;
 40   border-style: none;
 41   border-color: gray;
 42   background-color: #F1F0EB;
 43   -moz-border-radius: ;
 44   }
 45  
 46   table td {
 47   border-width: 0px;
 48   padding: 1px;
 49   line-height: 1.2em;
 50   border-style: none;
 51   border-color: gray;
 52   background-color: white;
 53   -moz-border-radius: ;
 54   }
 55   </style>
 56      </head>
 57        <body>
 58          <h1>Log date: <xsl:value-of select="/LogEntries/@Date"/> 
 59          [<xsl:value-of select="count(/LogEntries/LogEntry)"/> entries.]</h1>
 60          <table border="1">
 61            <thead>
 62              <tr bgcolor="#9acd32">
 63                <td>Date Time</td>
 64                <td>Severity / Exception type</td>
 65                <td>Message</td>
 66                <td>Source / ID + Stacktrace</td>
 67                <td>ThreadId</td>
 68              </tr>
 69            </thead>
 70            <tbody>
 71              <xsl:apply-templates select="*"/>
 72            </tbody>
 73          </table>
 74        </body>
 75      </html>
 76    </xsl:template>
 77  
 78    <xsl:template match="LogEntry">
 79      <tr>
 80        <td>
 81          <xsl:value-of select="@Date"/>
 82        </td>
 83        <td>
 84          <xsl:value-of select="@Severity"/>
 85        </td>
 86        <xsl:apply-templates select="*"/>
 87        <td>
 88          <xsl:value-of select="@Source"/>
 89        </td>
 90        <td align="center">
 91          <xsl:value-of select="@ThreadId"/>
 92        </td>
 93      </tr>
 94    </xsl:template>
 95  
 96    <xsl:template match="LogEntry[@Severity='Exception']">
 97      <tr>
 98        <td>
 99          <xsl:value-of select="@Date"/>
100        </td>
101        <td>
102          <xsl:value-of select="@Severity"/>
103        </td>
104        <td>
105          <i>See details below</i>
106        </td>
107        <td>
108          <xsl:value-of select="@Source"/>
109        </td>
110        <td align="center">
111          <xsl:value-of select="@ThreadId"/>
112        </td>
113      </tr>
114      <xsl:apply-templates select="Exception"/>
115    </xsl:template>
116  
117    <xsl:template match="Message">
118      <td>
119        <xsl:value-of select="."/>
120      </td>
121    </xsl:template>
122  
123    <xsl:template match="Exception">
124      <tr>
125        <td/>
126        <td>
127          <xsl:value-of select="@Type"/>
128        </td>
129        <xsl:apply-templates select="Message"/>
130        <td>
131          <xsl:value-of select="@ID"/>
132          <xsl:value-of select="StackTrace"/>
133        </td>
134        <td/>
135      </tr>
136      <xsl:apply-templates select="Exception"/>
137    </xsl:template>
138  </xsl:stylesheet>

A small piece of code is added to the SimpleLog.cs file to determine the default browser on the target PC so the log file is shown in a browser irrespective of the XML file association.

Using the Code

The code works exactly the same as the original code from Jochen Scharr. The example provided in his article still works but now shows the output as a table in the default browser as shown in the screenshot above.

C#
static void Main(string[] args)
{
  // Log to a sub-directory 'Log' of the current working directory. 
  // Prefix log file with 'MyLog_'.
  // This is an optional call and has only to be done once, 
  // preferably before the first log entry is written.
  SimpleXmlLog.SetLogFile(".\\Log", "MyLog_");
 
  // Write info message to log
  SimpleXmlLog.Info("Test logging started.");
 
  // Write warning message to log
  SimpleXmlLog.Warning("This is a warning.");
 
  // Write error message to log
  SimpleXmlLog.Error("This is an error.");
 
  try
  {
    // For demonstration, do logging in sub-method, throw an exception,
    // catch it, wrap it in another exception and throw it.
    DoSomething();
  }
  catch(Exception ex)
  {
    // Write exception with all inner exceptions to log
    SimpleXmlLog.Log(ex);
  }
 
  // Show log file as a table in the default browser
  SimpleXmlLog.ShowLogFile();
}
 
private static void DoSomething()
{
  SimpleXmlLog.Info("Entering method. See Source which method is meant.");
 
  try
  {
    DoSomethingElse(null);
  }
  catch(Exception ex)
  {
    SimpleXmlLog.Log(ex);
    throw new InvalidOperationException("Something went wrong.", ex);
  }
}
 
private static void DoSomethingElse(string fred)
{
  SimpleXmlLog.Info("Entering method. See Source which method is meant.");
 
  try
  {
    // Purposely provoking an exception.
    int a = fred.IndexOf("Hello");
  }
  catch(Exception ex)
  {
    throw new Exception("Something went wrong.", ex);
  }
}

Points of Interest

Apart from adding the processing instructions to the exported XML, Jochen Schurr comments in the source code were reformatted so the help compiler output would look nicer.

History

  • 20th February, 2021: Updated source

Be sure to read the original article by Jochen Schurr to understand the details of using the Simple Log class.

License

This article, along with any associated source code and files, is licensed under The MIT License