Click here to Skip to main content
15,892,059 members
Articles / Programming Languages / C#
Article

Errors in XML Log with C#/.NET

Rate me:
Please Sign up or sign in to vote.
4.63/5 (10 votes)
23 Oct 20022 min read 185.6K   1K   40   29
How to write all your errors when using Console.Error to an XML file.

Introduction

Like other programmers I use a lot of try...catch statements in my code to catch exceptions, help debug, and avoid crashs. For that reason, I put a lot of Console.Error.WriteLine(...) in my catch blocks to keep a trace of the exceptions that occured. But when I release an app, even if exceptions should not happen anymore, it is always useful to have some kind of system to keep a trace of raised exceptions. The following code demonstrates a solution to keep a trace of those exceptions in a XML file.

The ErrorLogWriter class

This class inherits from the standard System.IO.TextWriter class. Subclassing TextWriter allows us to use our custom ErrorLogWriter class with Console.Error.
The trick is to create an ErrorLogWriter instance and then call Console.SetOut(...) with this instance as a parameter.
Then every Console.Error.WriteLine(...) call will be redirected to our ErrorLogWriter instance and a XML entry added in the file.
I tried to keep the code as simple as possible. So many features can be added, like handling more TextWriter.Write methods or providing even more informations in the ErrorLogWriter.WriteLine function.
To give this article some additional value, I also gave a way to get the calling class and method using the stack trace and reflection. I also added an attribute to the ErrorLogWriter.WriteLine function to synchronize it in multi-threaded programs (equivalent to the lock(this) keyword).

C#
using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace ErrorLogger.Utility{
    /// <summary>
    /// ErrorLogWriter class
    /// </summary>
    public class ErrorLogWriter:TextWriter{
        // Variables
        private bool Disposed;
        private XmlTextWriter Writer;

        // Constructor
        public ErrorLogWriter(string FilePath){
            Disposed=false;
            Writer=new XmlTextWriter(FilePath,Encoding.Unicode);

            // Write header
            Writer.Formatting=Formatting.Indented;
            Writer.WriteStartDocument();
            Writer.WriteStartElement("error");
            Writer.Flush();
            }

        // Destructor (equivalent to Finalize() without the need to call base.Finalize())
        ~ErrorLogWriter(){
            Dispose(false);
            }

        // Free resources immediately
        protected override void Dispose(bool Disposing){
            if(!Disposed){
                if(Disposing){
                    }
                // Close file
                Writer.Close();
                Writer=null;
                // Disposed
                Disposed=true;
                // Parent disposing
                base.Dispose(Disposing);
                }
            }

        // Close the file
        public override void Close(){
            // Write footer
            Writer.WriteEndElement();
            Writer.WriteEndDocument();
            Writer.Flush();
            // Free resources
            Dispose(true);
            GC.SuppressFinalize(this);
            }

        // Implement Encoding() method from TextWriter
        public override Encoding Encoding{
            get{
                return(Encoding.Unicode);
                }
            }

        // Implement WriteLine() method from TextWriter (remove MethodImpl attribute for single-threaded app)
        // Use stack trace and reflection to get the calling class and method
        [MethodImpl(MethodImplOptions.Synchronized)]
        public override void WriteLine(string Txt){
            Writer.WriteStartElement("event");
            Writer.WriteStartElement("time");
            Writer.WriteString(DateTime.Now.ToLongTimeString());
            Writer.WriteEndElement();
            Writer.WriteStartElement("class");
            Writer.WriteString(new StackTrace().GetFrame(2).GetMethod().ReflectedType.FullName);
            Writer.WriteEndElement();
            Writer.WriteStartElement("method");
            Writer.WriteString(new StackTrace().GetFrame(2).GetMethod().ToString());
            Writer.WriteEndElement();
            Writer.WriteStartElement("text");
            Writer.WriteString(Txt);
            Writer.WriteEndElement();
            Writer.WriteEndElement();
            Writer.Flush();
            }
        }
    }

The test class

Here is an example of how to use the ErrorLogWriter class. Of course, all this code is available in the zip file.

C#
using System;
using System.IO;
using ErrorLogger.Utility;

namespace ErrorLogger{
    /// <summary>
    /// Testing class
    /// </summary>
    class Test{
        /// <summary>
        /// Main loop
        /// </summary>
        [STAThread]
        static void Main(string[] args){
            // Here is the magic (file in .exe current directory)
            ErrorLogWriter Err=new ErrorLogWriter(Directory.GetCurrentDirectory()+@"\Error.xml");
            Console.SetError(Err);

            // Testing
            Console.Error.WriteLine("Here is my first error !");
            Console.Error.WriteLine("I should write this inside a catch(exception) statement...");
            // Inside another function
            newFunction();

            // Close error file
            Err.Close();

            // Wait for key
            Console.Out.WriteLine("Error file created. Press a key...");
            Console.Read();
            }

        // Just for the test
        private static void newFunction(){
            Console.Error.WriteLine("Look ! I am inside a function !");
            }
        }
    }

Conclusion

I hope this article will help some people and will be useful for somebody.
Here you have basic concepts to understand how to subclass the TextWriter class, how to redirect the standard Console.Error stream and some tips about reflection and stack trace.
It is also worth to add that you can customize this code to redirect other streams like Console.In or Console.Out.

Happy Coding !!!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Siliconz Ltd
New Zealand New Zealand
Richard Lopes
Just Programmer

Comments and Discussions

 
GeneralRe: Logging to XML Pin
reborn_zhang26-Nov-09 3:18
reborn_zhang26-Nov-09 3:18 
GeneralErrorLogger Pin
xptom9-Dec-02 10:10
xptom9-Dec-02 10:10 
GeneralRe: ErrorLogger Pin
GriffonRL9-Dec-02 21:59
GriffonRL9-Dec-02 21:59 
GeneralHere is an example output Pin
GriffonRL25-Oct-02 3:24
GriffonRL25-Oct-02 3:24 
GeneralRe: Here is an example output Pin
Vasudevan Deepak Kumar7-Nov-02 3:42
Vasudevan Deepak Kumar7-Nov-02 3:42 
GeneralRe: Here is an example output Pin
GriffonRL7-Nov-02 5:20
GriffonRL7-Nov-02 5:20 
GeneralInteresting approach Pin
Marc Clifton24-Oct-02 8:02
mvaMarc Clifton24-Oct-02 8:02 
GeneralRe: Interesting approach Pin
GriffonRL24-Oct-02 12:10
GriffonRL24-Oct-02 12:10 
Hello Marc,

I just finished reading your article and also Vagif's article, and I must admit that your articles are far more developed than mine Smile | :) . Your article is very clear. I had a nice overview of all the options available with .NET. I liked a lot your discovery about TRACE functions changed to DEBUG functions in release. I see we are several people submitting articles on this subject these days OMG | :OMG: .
I have looked at the Debug and Trace mechanisms of .NET, but not went enough deeply in that, so I missed the listener part. I think I could choose to use the TRACE mechanism and write an XML listener, but... I will be very interested to look at the next version of .NET including a better TRACE system (quoting Vagif). I will wait and see, and maybe my very simple solution will become useless.

But I do think using XML files as log files is a good option.
If you think of logging overhead, XML is certainly more expensive than a text file. But if you want to build a file you can manipulate easely in a wide range of tool, make it readable, or even integrate it into a database, XML is the choice today. I use XML also for my configuration files and provide the DTD files for description and validation.

The history of this logging system come from a similar system I wrote in Java 2 years ago. At this time no logging system was available for Java and an easy way to implement it was to redirect StdErr stream. StdIn, StdOut or StdErr are streams I already knew from C and are "low level" mechanisms. Today, TRACE or DEBUG rely on the same mechanisms. That's why I just made a port from Java to .NET. But it will make more sense with the .NET framework to subclass the TraceListener (you are right, thanks to your article Wink | ;) ).

Concerning the Stack and Reflection classes, it is worth the look. The .NET framework offer very large capabilities to control the source code or dynamically load a class and invoke a method (nice for plug-in systems).
Using the Stack class you can create a very powerful exception logging, including line numbers, call stack...
I am thinking of writing an article about building a plug-in system but I will first search CodeProject for an existing article on this subject Big Grin | :-D .
I am also thinking on building a tool to monitor in realtime another .NET application running (profiling) and log a trace history. I will first look for a free existing solution too.

Thanks,

R. LOPES
Just programmer.

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

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