Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
65 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Using XML Technologies For Enhancing Log Files


Prettifying XML using XSLT

At this point you should be able to output an XML listing for a given test-run of your application. Chances are you'll have a suitably large .xml file; which, if you look at it under Microsoft's Internet Explorer (you get a very similar output with Mozilla based browsers):

MSIE does a reasonable job of colour coding the different types of syntax that make up the document. The most useful thing it does is represent the document in it's true hierarchical form, one that you can collapse and expand using the "+" and "-" symbols next to parent elements:

You might also have noticed straight away that the structure I described previously in this article is now filled in with information about my development system. This is the information that I mentioned is readily available via the Win32 API.

The above, hierarchical (and read-only) output is fairly useful – you can navigate around it quite easily, albeit slowly.

However, using the wonders of XSLT, a much better output can be crafted. Using an XSLT file to accompany the XML document we can format any given (well formed) XML log file into a pre-determined HTML output.

As you may be aware, it is possible to use XSLT to transform XML into a variety of different formats and file types. This can be particularly useful if you run all of your output to XML now, but in a few years time wish to transform it into a bigger/better format.

XSLT is a very powerful formatting language, and can be used to create very complex representations from a relatively simple script. In this particular article we will be using it to generate a tabulated output that is colour coded to the contents of a given LogEvent.

Using XSLT template matching, the browser will iterate over any number of LogEvents contained within the document – the style sheet need only specify the formatting for a single instance.

The XML format discussed previously in this article was broken down into two main components – the LogHeader and then all subsequent LogEvents. The XSLT can also generate a formatted version of the header by extracting the parameters stored in relevant tags and inserting them into a readable context.

The following image shows how Internet Explorer renders this:

The light grey italic text is that read from the raw XML outputted by the software. The only exception is that the last line ("Total logged events: 100") is generated by the XSLT counting how many LogEvent constructs it finds in the file.

The following fragment of code is the XSLT that process a LogHeader construct:

<xsl:template match="LogHeader">

  <br/>
  <b>
    <font face="Arial" size="3" color="#000000">
      Log file header information:
    </font>
   </b>
   <br/>
   <font face="Arial" size="2" color="#000000">
     Output level:
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="OutputLevel"/>
     </font>
   </i>
   <br/>
   <font face="Arial" size="2" color="#000000">
     Session started at
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="Session/Started/Time"/>
     </font>
   </i>
   <font face="Arial" size="2" color="#000000">
     on
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="Session/Started/Date"/>
     </font>
   </i>
   <br/>
   <font face="Arial" size="2" color="#000000">
     Operating environment:
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="Session/Configuration/Environment"/>
     </font>
   </i>
   <br/>
   <font face="Arial" size="2" color="#000000">
     System processor:
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="Session/Configuration/Processor/Family"/>
     </font>
   </i>
   <font face="Arial" size="2" color="#000000">
     running at approximately
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="Session/Configuration/Processor/ClockSpeed"/>
     </font>
   </i>
   <font face="Arial" size="2" color="#000000">
     Mhz
   </font>
   <br/>
   <font face="Arial" size="2" color="#000000">
     Memory on start-up:
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="Session/Configuration/Memory/Available"/>
     </font>
   </i>
   <font face="Arial" size="2" color="#000000">
     available from
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:value-of select="Session/Configuration/Memory/Total"/>
     </font>
   </i>
   <font face="Arial" size="2" color="#000000">
     installed
   </font>
   <br/>
   <font face="Arial" size="2" color="#000000">
     Total logged events:
   </font>
   <i>
     <font face="Arial" size="2" color="#808080">
       <xsl:copy-of select="count(../LogEvent)"/>
     </font>
   </i>

</xsl:template>

There isn't anything in the above XSLT that is particularly groundbreaking, rather a simple case of formatting the incoming values accordingly. I mentioned before that the last line is run-time generated – and that can be seen in the form of a "count(../LogEvent)" statement towards the bottom. Note that this will actually execute the template match that I am about to describe – so any filter that guarantees no output from a LogEvent template match will not contribute to the count( ).

The next interesting part is formatting each LogEvent. In the initial root template match a table outline is configured using plain HTML:

<table border="1" width="100%" cellspacing="0" cellpadding="0"
    bordercolorlight="#000000" bordercolordark="#ffffff" bordercolor="#000000">
  <tr>
    <td width="3%"  bgcolor="#000000">
      <font size="2" face="Arial" color="#FFFFFF">
        <b>
          <center>#</center>
        </b>
      </font>
    </td>
    <td width="20%" bgcolor="#000000">
      <font size="2" face="Arial" color="#FFFFFF">
        <b>
          <center>Time</center>
        </b>
      </font>
    </td>
    <td width="23%" bgcolor="#000000">
      <font size="2" face="Arial" color="#FFFFFF">
        <b>
          <center>File</center>
        </b>
      </font>
    </td>
    <td width="50%" bgcolor="#000000">
      <font size="2" face="Arial" color="#FFFFFF">
        <b>
          <center>Function</center>
        </b>
      </font>
    </td>
    <td width="4%"  bgcolor="#000000">
      <font size="2" face="Arial" color="#FFFFFF">
        <b>
          <center>Line</center>
        </b>
      </font>
    </td>
  </tr>
  <xsl:apply-templates select="RunTimeLog/LogEvent"/>
</table>

That is, a table with 5 columns: event number, time index, source code file, enclosing function, originating line number. Once the first row of the table is created, the processing of LogEvents begins via the xsl:apply-templates call. It is designed such that each of the LogEvent template matches will add an additional two rows (why two? – wait and see!) to the table. Obviously, if there are no events in the log file or, as we'll see later on, none match the current filter we will be left with just the header for a table and no entries.

The XSLT code for formatting the LogEvent elements is quite a lengthy and repetitive piece of code – so I'll only highlight the important parts here. Later on you will be able to explore the code for yourself if you're particularly interested.

The LogEvent template essentially outputs two rows to the table – the first row with the actual details of the event, the second with the message. After some experimentation I found this to provide the most usable output for the simple reason that the "statistical" data attached is of a fairly consistent size, whereas the log message can be anything from a couple of words to a long sentence. HTML tables are a bit tricky to guesstimate the correct dimensions when you know that the data contained in a given cell can substantially vary in size.

The basic structure of the LogEvent template is as follows:

<xsl:template match="LogEvent">

  <xsl:choose>

    <xsl:when test="Type='Comment'">
      <tr bgcolor="#80FF80" valign="middle" align="center">

      </tr>
    </xsl:when>

    <xsl:when test="Type='Unknown'">
      <tr bgcolor="#EEEEEE" valign="middle" align="center">

      </tr>
    </xsl:when>

    <xsl:when test="Type='Error'">
      <tr bgcolor="#FF8080" valign="middle" align="center">

      </tr>
    </xsl:when>

    <xsl:when test="Type='Warning'">
      <tr bgcolor="#FFAA80" valign="middle" align="center">
      </tr>
    </xsl:when>
      <xsl:when test="Type='Event'">
      <tr bgcolor="#8080FF" valign="middle" align="center">

      </tr>
    </xsl:when>

    <xsl:when test="Type='Debug'">
      <tr bgcolor="#FFFF80" valign="middle" align="center">

      </tr>
    </xsl:when>

    <xsl:when test="Type='Game Message'">
      <tr bgcolor="#FF8020" valign="middle" align="center">

      </tr>
    </xsl:when>

  </xsl:choose>

</xsl:template>

Basically, we have an xsl:choose construct that varies the colour of a given row according to the LogEvent/Type classification. This colouration of the different categories works brilliantly when you are displaying a large subset (or all) of the log file.

An example of the XSLT/HTML code used within a given element ("comment" in the following example) is as follows:

<tr bgcolor="#80FF80" valign="middle" align="center">

  <td>
    <font size="2" face="Arial" color="#202020">
      <center>
        <xsl:value-of select="@id"/>
      </center>
    </font>
  </td>

  <xsl:apply-templates select="TimeIndex"/>
  <xsl:apply-templates select="File"/>
  <xsl:apply-templates select="Function"/>
  <xsl:apply-templates select="LineNumber"/>

</tr>
<tr bgcolor="#AAFFAA">
  <xsl:apply-templates select="Message"/>
</tr>

Outputs of the individual elements of a LogEvent are the same, so I've made use of template matching for each of them – this feature of XSLT is useful as it allows you to reduce repetition and make single changes that affect the whole template. The first row contains the 5 elements specified by the headers in the default template match, with a colour specific to the type of message. The second row spans the entire table and is of a slightly lighter shade of the same colour.

The specific template matches for the individual fields all appear to be the same, and follow this example:

<xsl:template match="TimeIndex">
  <td>
    <font size="2" face="Courier New" color="#404040">
      <center>
        <xsl:apply-templates/>
      </center>
    </font>
  </td>
</xsl:template>

Quite simply there is no special XSLT in the above fragment. The only reason for doing so is, as previously mentioned, to avoid repeating the same fragment of code for each of the xsl:choose statements above. As you should immediately see, if you were to modularize these pieces of output, you can easily change the formatting of a given field and have it instantly reflected across the whole document.

The above-demonstrated XSLT filtering of the log file generates the following output:

In the above image, you can see the first six entries of a basic log file. The top of the table, the header, is indicated in a black background with white text. The actual log events are indicated by the two-rows-per-event design detailed previously in this document. The colouring displayed in this document shows three of the output types – Debug (yellow), Event (blue) and Comment (green). Other colours are used, as shown in the previous layouts.

If you click here [17] you should be able to view the full XSLT document. If you click here [18] you can view the results of the full XML document as processed by the XSLT.





Enhancing XSLT using JavaScript


Contents
  Introduction
  Basic requirements and background
  Prettifying XML using XSLT
  Enhancing XSLT using JavaScript
  Conclusion

  Printable version
  Discuss this article