Click here to Skip to main content
Click here to Skip to main content

Use XML for Log Files

, 22 Aug 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
How to use XML to write and read log files.

NRSoftware

Introduction

Many applications write log files to get some information about what the application is doing. Especially, applications without a user interface use log files, like for example, Windows Services or Web applications. These log files are plain text files, normally.

Most often, one line of text corresponds to one log entry. If an entry contains several fields, these are separated by a delimiter, e.g., a semicolon. Here is a small example:

8/20/2008 3:35:06 PM ; Some log information ; some more log information
8/20/2008 3:39:09 PM ; Something happened ; Reason: unknown

The problem with text files: the delimiter may be part of the log information. When writing the log information, you have to prepend an escape character, indicating that the following character is not a delimiter, and reading the file has to take care of analyzing the escape characters as well.

Use XML

When using XML and the XML classes provided by .NET, you do not have to take care of delimiters. And, for reading and analyzing XML files, .NET has some powerful classes like XmlReader and XmlDocument, or the ReadXml method of the DataSet class.

But, there is a small problem with XML as well. Valid XML requires that the content is part of a root node. The opening tag of this root node must be at the beginning of the file, and the closing tag at the end.

When writing information to log files, normally, some method is used which creates the file if it does not exist, and then writes the information before closing the file. If the file exists, the information is appended to the file.

This makes it difficult to create the XML root node. Therefore, we will write the file without the root node. Unfortunately, we will have a file which does not have valid XML. But, for reading the file, we create a helper class XmlLogfileStream, which is derived from Stream. This class will prepend the opening tag of the root node, and append the corresponding closing tag at the end. Note that the file itself is not touched, only the input stream will provide these tags.

Now, when using classes like XmlDocument or XmlReader, you only have to use this stream class, and you get valid XML.

The Code for Writing to Log Files

This is very easy. Have a look at this sample:

private void WriteLogInformation(string filename, string info1, string info2)
{
     StringBuilder sbuilder = new StringBuilder();
     using (StringWriter sw = new StringWriter(sbuilder))
     {
         using (XmlTextWriter w = new XmlTextWriter(sw))
         {
             w.WriteStartElement("LogInfo");
             w.WriteElementString("Time", DateTime.Now.ToString());
             w.WriteElementString("Info1", info1);
             w.WriteElementString("Info2", info2);
             w.WriteEndElement();
         }
     }
     using (StreamWriter w = new StreamWriter(filename, true, Encoding.UTF8))
     {
         w.WriteLine(sbuilder.ToString());
     }
}

First, a XmlTextWriter is used to write the log information to a StringBuilder. The content of the StringBuilder is then written to the file using a StreamWriter. The second parameter of the constructor specifies that the file shall be created if it does not exist. If the file exists, the information will be appended to the file.

Calling this method with:

WriteLogInformation("test.logxml", "Trace Information <5>", 
                    "& some more Information 5");

will append this to the file test.logxml:

<LogInfo><Time>8/20/2008 3:35:06 PM</Time><Info1>Trace Information &lt;5&gt;</Info1>
            <Info2>&amp; some more Information 5</Info2></LogInfo>

Code for Reading the Log File

Using the class XmlLogfileStream (described below), we can read the file with:

private void ReadLogfile(string filename)
{
    using (XmlLogfileStream logfileStream = new XmlLogfileStream(filename))
    {
        DataSet ds = new DataSet();
        ds.ReadXml(logfileStream);
        dataGridViewLogfile.DataSource = ds;
        dataGridViewLogfile.DataMember = ds.Tables[0].TableName;
    }
}

This code creates the XmlLogfileStream and uses it to load the data into a DataSet with ReadXml. Binding the DataSet to a DataGridView will display the data.

Of course, it is also possible to use the class XmlLogfileStream with XmlDocument and XmlWriter. Here is an example for XmlReader.

using (XmlLogfileStream stream = new XmlLogfileStream(filename))
{
    using (XmlReader reader = XmlReader.Create(stream))
    {
        // use XmlReader as usual
    }
}

Class XmlLogfileStream

The code for the class XmlLogfileStream is included in the download. The class is derived from Stream. Internally, it uses a FileStream to access the file.

The interesting method is Read.

public override int Read(byte[] buffer, int offset, int count)
{
    if (_closingTagRead) return 0; // file completely read
    if (_firstTime)  // nothing read so far, therefore provide the opening tag
    {
        _firstTime = false;
        Byte[] data = System.Text.Encoding.ASCII.GetBytes("X<x>");
        //
        // this code is not foolprof. Here I return "X<x>" in one go, even if 
        // buffer does not provide enough space. Therefore the assert.
        // From experience I know that the buffer is always big enough.
        //
        Debug.Assert(data.Length <= count);
        for (int idx = 0; idx < data.Length; idx++)
            buffer[offset + idx] = data[idx];
        return data.Length;
    }
    int bytesRead = _fileStream.Read(buffer, offset, count);
    if (bytesRead == 0 && !_closingTagRead)   // all contents of file read, but 
                          // closing tag not provided yet
    {
        _closingTagRead = true;
        Byte[] data = System.Text.Encoding.ASCII.GetBytes("</x>");
        Debug.Assert(data.Length <= count);
        for (int idx = 0; idx < data.Length; idx++)
            buffer[offset + idx] = data[idx];
        return data.Length;
    }
    return bytesRead;
}

When this method is called for the first time, it does not access the file, but returns <X> in the parameter buffer. Subsequent calls return data from the file until all file data are read. Finally, the method returns the closing tag </X>, before returning a 0 to indicate the file has been read completely.

The Contents of the Download

The download contains the class XmlLogfileStream and a small Windows Forms application for testing. The application consists of one form. You can specify the name for the log file. A button is provided to write some lines to this file. This takes about 5 seconds, not because there are too many lines written, but because a call to Thread.Sleep was added to see some difference in the time stamps written to the file.

The second button reads the file and displays the contents in a DataGridView.

The code was developed using Visual Studio 2005 and .NET 2.0.

Improvements

Currently, the class XmlLogFileStream uses a FileStream internally. This stream could be provided as a parameter to the constructor (or a second constructor could be added) to make it possible to use it with other streams.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Norbert Ruessmann
freelance developer
Germany Germany
I studied physics at the university of cologne. After university I somehow got into software developemnt, starting with the Apple II.
Today I am focused on Windows developement using C# / .NET (Forms, ASP.NET, WPF) and sometimes C++.

Comments and Discussions

 
QuestionWhy pay the XML tax for a file thats pretty much only going ot be human readable? PinmemberAdamSane22-Aug-08 4:24 
AnswerRe: Why pay the XML tax for a file thats pretty much only going ot be human readable? PinmemberNorbert Ruessmann22-Aug-08 4:36 
AnswerRe: Why pay the XML tax for a file thats pretty much only going ot be human readable? PinmemberPIEBALDconsult22-Aug-08 5:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411022.1 | Last Updated 22 Aug 2008
Article Copyright 2008 by Norbert Ruessmann
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid