Click here to Skip to main content
Email Password   helpLost your password?

Back in the "Old days..."

How many times have you worked on a project and found yourself implementing your own custom application logging solution? How many more times have you been part of a large project where it seems that every programmer implements his or her own logging solution, and log files are strewn across the system? Instead of logging to a central log file or to the Event Log, messages are dumped to their own custom files, with arbitrary file formats and information. How many times have you told your customers that they would have to �Search for all files named *.log, and send them to us.� It�s certainly not confidence inspiring, and makes �debugging via end user� especially difficult.

Application logging is often an afterthought in application architectures, and log files are frequently found haphazardly distributed across the system. With the advent of .NET, Microsoft has declared application logging a first class citizen, embedding a solution right into the framework. Microsoft�s Trace classes offer simple logging, but also allow for extensive customization. In this article, we will explore the use of reflection to enhance trace logging in your current applications.

Background

There have been several good articles already written on Tracing, and I'll try to avoid reproducing them here. For a solid overview of tracing, you might visit CodeProject's own Writing custom .NET trace listeners by Vagif Abilov, or MSDN's February 2001 Bugslayer article.

Tracing

The Trace class is found in the System.Diagnostics namespace of the Framework. It�s simple and easy to use. You simply define TRACE in your application (VS.NET does this by default in all C# projects), and begin making calls to one of the static Write() methods found in the System.Diagnostics.Trace class.

using System.Diagnostics;
public void MyMethod() {
      
    Trace.WriteLine("Entering Method MyMethod()");
    // Rest of your application code.    

}

Making the Trace.WriteLine() call is simple, but who is listening to the call? Where does this information go? Microsoft designed tracing to be as customizable and flexible as possible, so that we can either ignore the information, or several different logging methods may subscribe as Listeners. The base class that allows for this is the TraceListener class. All logging solutions must derive from this class. One of the simplest listeners included in the framework is a pre-build logging solution called the TextWriterTraceListener. The TextWriterTraceListener allows us to log, trace output directly to a file, with minimal additional coding. All we have to do is notify the framework that the TextWriterTraceListener wishes to subscribe to the application's Trace messages. Notification may occur through adding the listener in the application code, or through the application�s config file. We�ll use the application config file, as it is the most flexible solution. Simply add the following code to the config file:

<configuration> 
  <system.diagnostics>
    <trace autoflush="true" indentsize="4">
      <listeners>
        <add name="CustomTraceListener" 
          type="Dennany.Diagnostics.CustomTraceListener,
            CustomTraceListener" initializeData="c:\myListener.log" /> 
        <remove type="System.Diagnostics.DefaultTraceListener"/>    
      </listeners>  
    </trace> 
  </system.diagnostics>
</configuration>

Make certain that the application config file is in the application�s directory, and is properly named. (See the downloadable code example TraceTester for a real-world example, or review the MSDN documentation for an in-depth discussion). A word of caution: config file errors are extremely difficult to track down, and simple errors like case sensitivity may cause failures that are difficult to find. You've been warned, so check your config file carefully! Other than that word of caution, that�s all there is to configuring a TextWriterTraceListener. All trace calls coming from the test application will now be directed to the file myListener.log. With tracing, it is easy to log to a file, the Event Log, or any other custom listener we wish to set up. However, after spending time with the trace class, I found myself writing a lot of boilerplate code. When I entered a method, I would write things like this:

public void MyMethod() {
    Trace.WriteLine("Entering Method MyMethod()");
    // Rest of your application code...

}

This isn�t really the ideal solution. I know it�s trivial, but what if I re-factor my program and change the name MyMethod to FooMethod? I�ve got to go in and change the Trace messages as well. Although this is a minor detail, it�s the kind of thing that has bitten me in the past, and bug hunting is difficult enough without having to be concerned about whether or not your trace messages are truly accurate.

So, reaching into my handy .NET toolbox, I pull out another invaluable tool to every .NET developer, Reflection. Using the Reflection classes, I am able to self examine the code at run-time, to determine who I am (what type am I?) and where I am (what method am I?). So, instead I found myself writing the following code:

using System.Reflection;
using System.Diagnostics;

public static void MyMethod() {
    Trace.WriteLine("Entering" + MethodInfo.GetCurrentMethod().
        ReflectedType.ToString() + "." + 
        MethodInfo.GetCurrentMethod().ToString() );
    // Rest of your application code.

}

With the above tracing call, the expected output sent to our TraceListener would be something like:

Entering MyNameSpace.ClientStartup.Void MyMethod()

Well, I�ve got my logging solution, and I�ve started to use it throughout my application, but there are several problems with the way I�ve done things. First, there is quite a bit of code here, and I�ve created quite a bit of IDE inheritance or cut and paste coding. It isn�t exactly the OO solution that we are looking for. If I were a C programmer, perhaps I would look to a pre-processor solution and create a macro that would simply copy this code in wherever I placed the macro. That way, if I ever wanted to update the code, I could do so in one place. ATL and MFC made heavy use of macros, and didn�t it work for them?

Aside: I�ve also made another noteworthy mistake here. I�ve assumed that the object�s .ToString() method returns the object�s type information. This is true for the majority of the FCL, but not all classes display this behavior. For example, the System.Int32.ToString() instance method is overloaded to return the String representation of the Int32, and not its type information. We'll address this later in the article.

Well, macro support was not included in C#, and there were very good reasons for this. The main reason was likely to keep people like me from doing dumb things like creating macros when we really shouldn�t! What else could I do? I could try re-factoring this code into a central logging class, but then whenever I call GetCurrentMethod(), I�ll get the name of the logging class, and not the name of MyMethod(). I looked at in-lining the code, but the C# compiler does not allow you to force the inlining of a method into another method.

The next immediate thought would be to extend the Trace class with my own custom Trace class that does all of the logging for me! That should work, shouldn�t it? Well, it doesn�t, because Microsoft has headed us off at the pass on this one by declaring the Trace class sealed. This means that inheritance is impossible, and any solution that I implement must be done completely from scratch. Since re-inventing the wheel is normally something that I would like to avoid, I�ll continue my search elsewhere. Besides this, there are a couple of other problems with the above solution that we haven�t addressed yet.

First, there is the issue of performance. Each time you execute the above Trace.WriteLine()call, several things happen:

The static method GetCurrentMethod() is called inside the WriteLine() call (twice!), the get_ReflectedType accessor method is called internally to return the type, and there are also the ToString() calls on each of the returned objects. Quite a bit of overhead for a bit of simple logging. All this is happening whether or not we are listening! This could be quite a performance hit on any application.

We could of course compile without the TRACE switch defined. In VS.NET, see Project -> Properties -> Configuration Properties -> Build -> Conditional Compilation Constants. The default is to define TRACE. If you remove this, then Trace calls will not be compiled into the final executable. The problem here is that you�ve made a compile time decision, and not a runtime one. It would be far better to be able to define this at the customer�s site, and not while you are sitting at your desk compiling the solution.

You could also use TraceSwitch to check to see if you should log, and then add additional code elsewhere in the application or in the config file to set this:

using System.Reflection;

// Create a TraceSwitch.

static TraceSwitch mySwitch = new TraceSwitch("mySwitch",
    "This is the description for mySwitch");

public static void MyMethod() {

    Trace.WriteLineIf(mySwitch.TraceVerbose, "Entering" +
        MethodInfo.GetCurrentMethod().ReflectedType.ToString()
        + "." + MethodInfo.GetCurrentMethod().ToString() );

    // Rest of your application code.

}

In fact, if you take a look at TraceSwitch in the MSDN docs, this is very similar to what they've done. However, now we�ve created a real monster � these logging calls seem to grow and grow. This will enable us to turn off logging at runtime, but we�ve added to our code-bloat. There are now several lines of code to update every time I need to make a change to the way I do logging.

Well, I�ve looked around the Trace class and am unable to find an elegant solution for my logging problem. I�ve found a way to auto generate and log, class information at runtime, but it is slow, a little bloated, and I don�t consider it to be maintainable.

The answer to my problem is certainly not on the Trace side of .NET�s logging solution. What about on the TraceListener side?

I�ve already taken a look at the TextWriterTraceListener class, and found that it is a simple and easy way to consume Trace messages sent from our test application. Would it be possible to extend our listener to take care of the above problem?

At first glance, we appear to be stuck with the same problem that we had when we looked at factoring the Trace calls into their own logging method. Any self-introspective reflection calls will return the name of the TraceListener subclass, and not the name of the class calling the Trace.WriteLine() method.

However, reaching back into our .NET class library toolbox, we find the StackFrame class tucked away in the System.Diagnostics namespace. Perhaps if we could walk through the callstack, we could find our way backwards to the class and method that called us?

A CustomTraceListener

It's pretty simple to extend the FCL's TextWriterTraceListener class, so we'll start with an example that adds the date / time stamp to each message logged by the listener:

public class CustomTraceListener : TextWriterTraceListener {

    // for our constructors, explicitly call the base 

    //class constructor.


    public CustomTraceListener( System.IO.Stream stream, 
        string name ) : base(stream, name) { }

    public CustomTraceListener( System.IO.Stream stream) :
        base(stream) { }
    public CustomTraceListener( string fileName, string name ) :
        base(fileName, name) {   }
    public CustomTraceListener( string fileName ) :
        base(fileName) { }
    public CustomTraceListener( System.IO.TextWriter writer, 
        string name ) : base(writer, name) { }
    public CustomTraceListener( System.IO.TextWriter writer ) :
        base(writer) { }
   
    public override void Write( string message ) {

        base.Write( getPreambleMessage() + message );
    }

    public override void WriteLine( string message ) {

        base.WriteLine( getPreambleMessage() + message );
    }

    private string getPreambleMessage() {
        StringBuilder preamble = new StringBuilder();
        preamble.Append(DateTime.Now.ToString());
        preamble.Append(": ");

        return preamble.ToString();
    }
}

In the above code sample, we've inherited from the TextWriterTraceListener class, and each of our constructors merely call the base class constructor. Each call to Write() or WriteLine() then calls the private method getPreambleMessage(). This method will be responsible for building the message that we want to pre-pend to our messages in our custom logging solution. Note that here we used StringBuilder for efficiency, as we will be building on this method later. For now, we are simply return a string containing the current date and time, and our Write() and WriteLine() methods will write the preamble followed by the message passed from the original Trace call.

That's really all there is to writing a simple custom listener - most of the work has already been done for us by the .NET Framework Class Library. Let's build upon this now.

StackFrame and StackTrace

Now that we've built a Listener, we've also got a bit of code that we can step through in a debugger and see exactly what is going on when we are listening. If we build a simple client console application that makes a simple Trace.WriteLine("test 1 , 2 3...") call, and set a breakpoint in our getPreambleMessage(), we might see a call stack similar to the one found in Figure 1. You might wish to download the sample code included and step through the debugger yourself.

A detailed look at the call stack (See Figure 1) shows us that when our application makes a Trace call, the framework calls an internal TraceInternal class, which then checks for any registered listeners. TraceInternal then notifies each listener of the Trace() call. This is easy enough to see in our Visual Studio .NET debugger, but how do we find this information dynamically at run time? I have to find a way to walk back through the call stack, skipping any of the internal Tracing calls, to the method that originally called my custom TraceListener. After retrieving this information, I would like to log the type information and method signature.

Figure 1: CustomTraceListener CallStack

When looking at the Call Stack in the Visual Studio debugger, you might notice that the only methods that are between the calling class and our CustomTraceListener are from the System.Diagnostics.Trace and TraceInternal classes. It is useful to note that these are both in the System namespace.

By getting the current StackTrace, I can find my location on the call stack using the StackTrace instance method GetFrame(). This tells me where I am now. More useful in my logging solution is where I was before. I may find this out by �walking the stack� backwards. How do I know where to stop on the stack? If you noticed in Figure 1 that Trace and TraceInternal are the only classes between us and the original calling method, you might be tempted to hard code the number of steps to backtrack on the stack. The major problem with this approach is that the release version of your code is free to perform inlining and various other optimization tricks that will �break� your code and be very difficult to debug. You are also tying yourself to a specific implementation and version of the CLR. A much slower, yet safer, solution is to use reflection to examine the name of each class and decide if we have moved far enough backwards on the call stack. I know that the name of my current class is CustomTraceListener, and I also know that everything between my calling method and me lies within the System namespace. Therefore, I will simply keep walking backwards on the call stack until I�m no longer in the CustomTraceListener class, and no longer in the System namespace. The following code snippet illustrates this principle:

StackTrace stackTrace = new StackTrace();
StackFrame stackFrame;
MethodBase stackFrameMethod;
int frameCount = 0;
string typeName;
do {
    frameCount++;
    stackFrame = stackTrace.GetFrame(frameCount);
    stackFrameMethod = stackFrame.GetMethod();
    typeName = stackFrameMethod.ReflectedType.FullName;
} while ( typeName.StartsWith("System") ||
    typeName.EndsWith("CustomTraceListener") );

Reflection: "Where were I?"

We now know how to find where we were, using the call stack information available to us at runtime, and we have already examined how to use reflection to retrieve class and method information. By combining the two, we may achieve our previous goal of finding "Who am I?" and "Where am I?" We'll accomplish this by replacing our getPreambleMessage() in our CustomTraceListenerClass with the following code:

private string getPreambleMessage(){
    StringBuilder preamble = new StringBuilder();
    StackTrace stackTrace = new StackTrace();
    StackFrame stackFrame;
    MethodBase stackFrameMethod;
    int frameCount = 0;
    string typeName;
    do {
        frameCount++;
        stackFrame = stackTrace.GetFrame(frameCount);
        stackFrameMethod = stackFrame.GetMethod();
        typeName = stackFrameMethod.ReflectedType.FullName;
    } while ( typeName.StartsWith("System") ||
        typeName.EndsWith("CustomTraceListener") );
    
    //log DateTime, Namespace, Class and Method Name

    preamble.Append(DateTime.Now.ToString());
    preamble.Append(": ");
    preamble.Append(typeName);
    preamble.Append(".");
    preamble.Append(stackFrameMethod.Name);
    preamble.Append("( ");

    // log parameter types and names

    ParameterInfo[] parameters = 
        stackFrameMethod.GetParameters();
    int parameterIndex = 0;
    while( parameterIndex < parameters.Length ) {
        preamble.Append(parameters
            [parameterIndex].ParameterType.Name);
        preamble.Append(" ");
        preamble.Append(parameters[parameterIndex].Name);
        parameterIndex++;
        if (parameterIndex != parameters.Length ) 
            preamble.Append(", ");
    }
    preamble.Append(" ): ");
    return preamble.ToString();
}

The above code example merely combines each topic we've discussed so far. For a full code example, you may download the CustomTraceListener code example linked to this article.

Looking at Reflection

When I first started learning .NET, I viewed reflection as a cool feature, but struggled with the idea that it could have any useful application for the day to day coder. After spending some time with it, however, I realized that reflection really opens the doors to more aspect-oriented programming, and will help greatly reduce coding efforts required to perform plumbing-type coding jobs. The more I look at reflection, the more I like what I see!

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralLicense
avirabinovich
6:39 19 May '09  
I want to use the code you have provided in a comercial company.
Is it ok to use it in my product?
Is the license is The Code Project Open License (CPOL) ?
QuestionTrace.Indent() causes issues...
Member 990375
5:52 18 Feb '09  
Great article. Just what I was looking for.

One issue though:

What appears to be happening is Indent calls the Write method, which ends up calling the custom Write method in your CustomTraceListener, which will then add the preamble.

The next call to the Trace.Write (or Trace.WriteLine) will add the same preamble again. The more indents you have, the more repeated preamble.

With the following code:
        private void button1_Click(object sender, EventArgs e)
{
Trace.WriteLine("Enter");
//Trace.Indent();

DoSomething();

//Trace.Unindent();
Trace.WriteLine("Exit");
}

private void DoSomething()
{
Trace.WriteLine("Enter");
//Trace.Indent();
DoSomethingElse();
//Trace.Unindent();
Trace.WriteLine("Exit");
}

private void DoSomethingElse()
{
Trace.WriteLine("Enter");
//Trace.Indent();
DoNothing();
//Trace.Unindent();
Trace.WriteLine("Exit");
}

private void DoNothing()
{
Trace.WriteLine("Enter");
//Trace.Indent();
Trace.WriteLine("Doing Nothing Here");
//Trace.Unindent();
Trace.WriteLine("Exit");
}


Without using the Trace.Indent (and Trace.Unindent), we get this, which is pretty nice:
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.button1_Click( Object sender, EventArgs e ): Enter
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.DoSomething( ): Enter
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): Enter
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.DoNothing( ): Enter
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.DoNothing( ): Doing Nothing Here
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.DoNothing( ): Exit
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): Exit
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.DoSomething( ): Exit
2/18/2009 10:34:44 AM: FIMCTraceListenerTester.Form1.button1_Click( Object sender, EventArgs e ): Exit


When using the Trace.Indent (and Trace.Unindent), we get this ugly trace:
2/18/2009 10:23:17 AM: FIMCTraceListenerTester.Form1.button1_Click( Object sender, EventArgs e ): Enter
2/18/2009 10:23:31 AM: FIMCTraceListenerTester.Form1.DoSomething( ): 2/18/2009 10:23:31 AM: FIMCTraceListenerTester.Form1.DoSomething( ): Enter
2/18/2009 10:24:24 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): 2/18/2009 10:24:24 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): 2/18/2009 10:24:24 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): Enter
2/18/2009 10:24:57 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:24:57 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:24:57 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:24:50 AM: FIMCTraceListenerTester.Form1.DoNothing( ): Enter
2/18/2009 10:25:46 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:25:46 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:25:46 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:25:46 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:25:46 AM: FIMCTraceListenerTester.Form1.DoNothing( ): Doing Nothing Here
2/18/2009 10:25:47 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:25:47 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:25:47 AM: FIMCTraceListenerTester.Form1.DoNothing( ): 2/18/2009 10:25:47 AM: FIMCTraceListenerTester.Form1.DoNothing( ): Exit
2/18/2009 10:25:54 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): 2/18/2009 10:25:54 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): 2/18/2009 10:25:54 AM: FIMCTraceListenerTester.Form1.DoSomethingElse( ): Exit
2/18/2009 10:26:01 AM: FIMCTraceListenerTester.Form1.DoSomething( ): 2/18/2009 10:26:01 AM: FIMCTraceListenerTester.Form1.DoSomething( ): Exit
2/18/2009 10:26:05 AM: FIMCTraceListenerTester.Form1.button1_Click( Object sender, EventArgs e ): Exit


Any thoughts on a solution to this issue?


Also, after calling Trace.Indent(), the indentation is appears AFTER the preamble, not prior to the preamble.

Just wanted to bring your attention to this.

Paul
GeneralRetrieving actual parameters
MicheleLaPietra
5:41 15 Apr '08  
Is it possible to retrieve actual parameters as well in the trace file?
GeneralA couple note
HummerX
5:12 16 Dec '07  
Good article, I found your article while searching for a good solution for a tracing class.

A few thing that come to mind that might help you and other in debugging. The JIT compiler will inline and do certain optimization (especially on the release build) I think that it will always report the correct stack frame with debug build to aid with debugging. So the reported stack frame might be off a couple frame... Just keep this in mind so you don't find yourself going down on a wild goose chase.
GeneralAlternate solution?
ergosum
11:16 4 Dec '05  
Jerry,

I fully understand the inspiration for your article, and you have done a great job bringing out some of the shortcomings of the Trace class.

But I'm not sure that building a Custom Trace Listner is the best solution to the problem. I'd still like to solve this problem while throwing the trace, as opposed to when listening to it. This way, I can direct output to DefaultTraceListner, TextWriterTraceListner or any other listner that I want.

For instance, consider this implementation


class MyTrace
{
private static TraceSwtich mySwitch = new TraceSwitch("MySwitch", "Description");

public static Trace (TraceLevel level, string message)
{
StringBuilder msg = null;
StackTrace st = null;
StackFrame sf = null;

if (level <= mySwitch.Level)
{
sf = st.GetFrame(1);
msg = new StringBuilder(DateTime.Now.ToString());
msg.Append("::");
msg.Append(sf.Getmethod());
msg.Append("::");
msg.Append(message);
}
}
}


Now, to use this class:

MyTrace.Trace(TraceLevel.Info, "Informational Trace");


This keeps my logic flow simple, and traces on demand only (thanks to TraceSwitch). I'm also free to a Win32 Debug Capturer (like DebugView) or a TextWriterTraceListner to listen to my traces.

Please feel free to point on if I have made any incorrect assumptions.

Thanks.
GeneralRe: Alternate solution?
ergosum
11:20 4 Dec '05  
Of course, I forgot to add

Trace.WriteLineIf(mySwitch.Level, msg.ToString());


at the end of the "if" block Smile.
GeneralRe: Alternate solution?
Jerry Dennany
15:00 31 Jan '06  
No, you've made no incorrect assumptions. I agree with you that in many cases there is a more compelling reason to perform changes on the tracing side, instead of the listening side.

The only constraint that your solution has is that you tightly bind your stack tracing mechanism at compile time. The original design of tracing allows for runtime binding through the use of application configuration.

If that's not an issue, then your solution is perfectly fine, and does have some advantages over the original article.

Thanks much!

-Jerry Dennany
Questionruntime error when debugging in whidbey
rajesh_gsi
4:00 5 Nov '05  
I am getting the following error....on the line

Line:
Trace.WriteLine("test 1 , 2 , 3...");

Error:
An unhandled exception of type 'System.Configuration.ConfigurationErrorsException' occurred in System.Configuration.dll

Additional information: Unrecognized attribute 'type'. Note that attribute names are case-sensitive.

plz help me
:(


rajesh
Questionerror on running the solution.......
rajesh_gsi
3:58 5 Nov '05  
I am getting the following error while executing the sln....
Additional information: Unrecognized attribute 'type'. Note that attribute names are case-sensitive
plz help me
:(

rajesh
AnswerRe: error on running the solution.......
Jerry Dennany
7:34 7 Nov '05  
Hi, Rajesh.

It looks like you are using Visual Studio 2005. This code was originally written for .NET 1.0 on Visual Studio.NET 2002. Since then, it looks like Microsoft has made a 'breaking' change.

Try changing the contents of the App.config file to the following:



<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<add name="CustomTraceListener"
type="Dennany.Diagnostics.CustomTraceListener,CustomTraceListener"
initializeData="c:\myListener.log" />
<remove name="System.Diagnostics.DefaultTraceListener"/>
</listeners>
</trace>
</system.diagnostics>
</configuration>


GeneralConfigure the Listener file name dynamically
Gajalakshmi Krishnan
11:11 1 Aug '05  
How to add the tracelistener file automatically rather than entering into appconfig ??
GeneralRe: Configure the Listener file name dynamically
Jerry Dennany
9:41 2 Aug '05  
Create a new instance of the TraceListener:

CustomTraceListener listener = new CustomTraceListener("c:\\path_to_tracefile\\Trace.txt");


and register the tracelistener:

System.Diagnostics.Trace.Listeners.Add(listener);


It's really that simple...


-Jerry Dennany
GeneralRe: Configure the Listener file name dynamically
Gajalakshmi Krishnan
11:45 3 Aug '05  
Excellent article .. Thanks a lot for your reply. It helped me a lot. I would like you to add EventLogger also along with.

Thanks,
Lakshmi
GeneralRe: Configure the Listener file name dynamically
MikeTheFid
11:39 17 Oct '07  
Don't forget to add

Trace.Close();

...before the app shuts down. This flushes the buffer and closes the listeners.

BTW - fantastic article.
GeneralExcellent, but...
atopcom
11:04 30 May '05  
In my case it does not work when I build a solution in the Release mode. In the Debug mode everything is OK. Somehow the reflection call stack is different at Release mode. I'm new in .Net maybe I do something wrong. Any ideas?

Thanks again,
markConfused
GeneralRe: Excellent, but...
Jerry Dennany
19:17 2 Jun '05  
It would be interesting to see your code sample to duplicate this issue. Perhaps you could email it to me?

jerry@dennany.org


Thanks!
GeneralRe: Excellent, but...
Troy Russell
11:47 24 May '09  
Ola,

  
   Pero, yo tengo un poquieto de su respuestas y todo de las aqethjw57.



                                                            Troy
Generalgreat article
scottfm
5:49 8 Apr '05  
thanks!
GeneralExcellent Article thank you
Angel_Komarov
15:06 8 Feb '05  
It is really well presented covering a very useful topic.

ak
GeneralGood Article Thanks
gerr
10:12 31 Jan '05  
Thanks, I will use this great idea rather than having to write a method's signature in my line of logging code in the methods.

GeneralObfuscation...
Stu McGill
5:45 10 Jan '05  
Thanks for this article - I learnt a lot from it and was going to use the same approach. But in case anyone is thinking of using Reflection for logging in production code it's worth thinking about obfuscation - if you obfuscate your code, the stack trace will contain meaningless class, method, and parameter names...

This is not a problem with this article, any technique using Reflection will have the same issues. It's just a bit more severe here as you want to be able to log from anywhere and so can't exclude anything from renaming.

In my experience obfuscation is usually left until the end of a project, so I thought it would be worth highlighting this to save anyone else the same problems/embarrassment I had.

Stuart
GeneralRe: Obfuscation...
Jerry Dennany
6:14 8 Apr '05  
Excellent point! Thanks for bringing that up.
GeneralLogging
pat270881
13:21 5 Aug '04  
hello,

I look in the Pocket PC API after classes which support in any wise to log the actions and/or tasks or commands a user executes on a PDA.(I need it for Usability Tests.)

Can anybody probably give me any tipps for which names and/or classes or things i have to look for and/or help me? this would be very important for me!

Thanks in advance.

yours sincerely,

patrick
GeneralExcellent- here's another idea
ToolmakerSteve2
17:14 9 Dec '03  
Thank you so much for writing this!

One idea:
there IS a concise way to do what you attempted early in the article,
when you inserted Trace.WriteLine( ...MethodInfo.GetCurrentMethod()... ).

(You then abandoned this, not wanting to insert too much code,
and not able to get the correct method name once you'd called some other method.)

Long messy cut & paste would be poor style, as you say.
But a concise cut & paste of a self-explanatory line of code would be a fine style.

A simple solution is to have each method do the "MethodInfo.GetCurrentMethod()" call,
and pass that as a parameter to a routine that does all the gunky details.
That way, what is inserted into each method is a simple, identical, line of code,
rather than all the gunk.

I've extended your example to give both "Entering" and "Exiting" messages.

NOTE: I am NOT suggesting this approach is superior to what you explore later in the message.
But its good to have Alternative Solutions - and this is an easy one to implement. Smile

Code details:
------------------------------------
public static void MyMethod1() {
MyTrace.Entering( MethodInfo.GetCurrentMethod() );
// ...
MyTrace.Exiting( MethodInfo.GetCurrentMethod() );
}
...
public static void MyMethod2() {
MyTrace.Entering( MethodInfo.GetCurrentMethod() );
// ...
MyTrace.Exiting( MethodInfo.GetCurrentMethod() );
}
...
public class MyTrace
{
public static void Entering(MethodBase currentMethod)
{
Msg( currentMethod, "Entering" );
}
public static void Exiting(MethodBase currentMethod)
{
Msg( currentMethod, "Exiting" );
}
private static void Msg(MethodBase currentMethod, String msg)
{
Trace.WriteLine(msg + ": " +
currentMethod.ReflectedType.ToString() +
"." + currentMethod.ToString() );
}
}
------------------------------------
-- ToolMakerSteve
GeneralRe: Excellent- here's another idea
Jerry Dennany
7:27 10 Apr '04  
Thanks for the excellent comment! You are correct in your suggestion, and I'm glad that you've added your code to your comments. If I ever revise this article, I will be certain to address this.


Last Updated 4 Mar 2003 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010