Nice logs in .NET

· Read in about 3 min · (581 words) ·

Been off the blog for sometime now. Been a bit busy with work and also with preparations for marriage (on Nov 7th) so haven’t got much time in between to post.

Here’s something interesting (and hopefully useful).

I’ve always been a fan of Log4Net (http://logging.apache.org/log4net). However, on this occasion, I had to use something that was custom built (using open source doesn’t go well with certain customers). So decided to look into System.Diagnostics. The overall idea seems to be pretty simple - configure a set of trace listeners, send your trace requests to a static Trace class. The Trace class will forward the calls to each of the listeners in sequence. There’s also a mechanism for multi-level logging - from nothing on to most verbose (None, Errors, Warnings, Information and Verbose). This is somewhat like the levels in log4net, but surprisingly, the Trace interface expects you to check for the switch each time you want to make a call. For example

Traceswitch ts = new Traceswitch("myswitch");
. . .
Trace.WriteLineIf(ts.TraceInfo, "this message will be logged if ts.level <= TraceLevel.TraceInfo);

Frankly, its too much work for me. Also, it is inefficient as the parameters will be built to a temporary object even if the trace level results in a no-op.

It sounds pretty basic - and believe me, it is. It just logs the string that it’s passed. Also, there’s no way to have different listeners that can each log at a different level - something like I want my file log to be at verbose level but want an event log listener that sends errors to the event log.

So, here’s something that I whipped up that gets over (some of) these limitations.

My requirements were 1. Include a timestamp 2. Include caller information 3. Include an easier API for app programmers (me)

For the first, the easiest thing to do is to subclass TextWriterTraceListener and override the Write and the WriteLine methods.

The second and third require a wee bit more thought. One solution is to replace Trace class with another class that provides a more convenient interface. I called mine TraceExt with the following methods

  • WriteInformation

  • WriteError

  • WriteWarning

  • WriteVerbose

Each method has two overloaded versions - one that takes a plain string and another that takes a format string and object array as in string.Format.

Another point worth mentioning is that the TraceExt is not a static class. Rather it’s expected that the class that uses TraceExt will keep a static member.

The TraceExt ctor takes the name of the trace switch to use. It instantiates the trace switch and then on, the calls to the WriteXXX are forwarded to Trace.WriteLine if the switch has the necessary level. A sample use of the TraceExt follows:

public class MainClass {
   private static TraceExt log = new TraceExt("myswitch");
   public static int Main(string args[]) {
    Trace.listeners.add(new TextWriterTraceListener("c:\some.log");
    log.WriteEntry();
    log.WriteInformation("This is some string");
    log.WriteExit();
    }
}

Each of the WriteXXXX methods are implemented as follows:

public void Information(string format, params object[] args){
    if (ts.TraceInfo){
       WriteLine(format, args);
    }
}

The WriteLine method is used to include other information in the logs like the caller, the method name etc. A little bit of reflection is used to get such information.

StackTrace trace = new StackTrace();
StackFrame frame = trace.GetFrame(0);
string callingMethod = frame.GetMethod().Name;
string callingClass = frame.GetMethod().DeclaringType.Name;
string callingNS = frame.GetMethod().DeclaringType.Namespace;

Sadly, just found out that I can’t upload the solution file zip. If you’re interested, comment on the article and leave your email id.