tags:

views:

6063

answers:

8

For a given class I would like to have tracing functionality i.e. I would like to log every method call (method signature and actual parameter values) and every method exit (just the method signature).

How do I accomplish this assuming that:

  • I don't want to use any 3rd party AOP libraries for C#,
  • I don't want to add duplicate code to all the methods that I want to trace,
  • I don't want to change the public API of the class - users of the class should be able to call all the methods in exactly the same way.

To make the question more concrete let's assume there are 3 classes:

 public class Caller 
 {
     public static void Call() 
     {
         Traced traced = new Traced();
         traced.Method1();
         traced.Method2(); 
     }
 }

 public class Traced 
 {
     public void Method1(String name, Int32 value) { }

     public void Method2(Object object) { }
 }

 public class Logger
 {
     public static void LogStart(MethodInfo method, Object[] parameterValues);

     public static void LogEnd(MethodInfo method);
 }

How do I invoke Logger.LogStart and Logger.LogEnd for every call to Method1 and Method2 without modifying the Caller.Call method and without adding the calls explicitly to Traced.Method1 and Traced.Method2?

Edit: What would be the solution if I'm allowed to slightly change the Call method?

A: 
  1. Write your own AOP library.
  2. Use reflection to generate a logging proxy over your instances (not sure if you can do it without changing some part of your existing code).
  3. Rewrite the assembly and inject your logging code (basically the same as 1).
  4. Host the CLR and add logging at this level (i think this is the hardest solution to implement, not sure if you have the required hooks in the CLR though).
kokos
+1  A: 

I don't know a solution but my approach would be as follows.

Decorate the class (or its methods) with a custom attribute. Somewhere else in the program, let an initialization function reflect all types, read the methods decorated with the attributes and inject some IL code into the method. It might actually be more practical to replace the method by a stub that calls LogStart, the actual method and then LogEnd. Additionally, I don't know if you can change methods using reflection so it might be more practical to replace the whole type.

Konrad Rudolph
+1  A: 

If you write a class - call it Tracing - that implements the IDisposable interface, you could wrap all method bodies in a

Using( Tracing tracing = new Tracing() ){ ... method body ...}

In the Tracing class you could the handle the logic of the traces in the constructor/Dispose method, respectively, in the Tracing class to keep track of the entering and exiting of the methods. Such that:

    public class Traced 
    {
        public void Method1(String name, Int32 value) {
            using(Tracing tracer = new Tracing()) 
            {
                [... method body ...]
            }
        }

        public void Method2(Object object) { 
            using(Tracing tracer = new Tracing())
            {
                [... method body ...]
            }
        }
    }
Steen
+1  A: 

Take a look at this - Pretty heavy stuff..http://msdn.microsoft.com/en-us/magazine/cc164165.aspx

Essential .net - don box had a chapter on what you need called Interception. I scraped some of it here (Sorry about the font colors - I had a dark theme back then...)http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html

Gishu
+9  A: 

C# is not an AOP oriented language. It has some AOP features and you can emulate some others but making AOP with C# is painful.

I looked up for ways to do exactly what you wanted to do and I found no easy way to do it.

As I understand it, this is what you want to do:

[Log()]
public void Method1(String name, Int32 value);

and in order to do that you have two main options

  1. Inherit your class from MarshalByRefObject or ContextBoundObject and define an attribute which inherits from IMessageSink. This article has a good example. You have to consider nontheless that using a MarshalByRefObject the performance will go down like hell, and I mean it, I'm talking about a 10x performance lost so think carefully before trying that.

  2. The other option is to inject code directly. In runtime, meaning you'll have to use reflection to "read" every class, get its attributes and inject the appropiate call (and for that matter I think you couldn't use the Reflection.Emit method as I think Reflection.Emit wouldn't allow you to insert new code inside an already existing method). At design time this will mean creating an extension to the CLR compiler which I have honestly no idea on how it's done.

The final option is using an IoC framework. Maybe it's not the perfect solution as most IoC frameworks works by defining entry points which allow methods to be hooked but, depending on what you want to achive, that might be a fair aproximation.

Jorge Córdoba
In other words, 'ouch'
johnc
I should point out that if you had first class functions, then a function could be treated just like any other variable and you could have a "method hook" that does what he wants.
RCIX
+15  A: 

The simplest way to acheive that is probably to use PostSharp. It injects code inside you methods based on the attributes that you apply to it. It allows you to do exactly what you want.

Another option is to use the profiling API to inject code inside the method, but that is really hardcore.

Antoine Aubry
you can also inject stuff with ICorDebug but that is super evil
Sam Saffron
+1  A: 

You could potentially use the GOF Decorator Pattern, and 'decorate' all classes that need tracing.

It's probably only really practical with an IOC container (but as pointer out earlier you may want to consider method interception if you're going to go down the IOC path).

Stacy A
A: 

you need to bug Ayende for an answer on how he did it: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx

Liao
lol, the comments on that blog reference this question...
RCIX