Trace Context Aspects with PostSharp

In my previous post I wrote about a method context information gathering framework I wrote in an attempt to increase the amount of useful information in trace output and exceptions. In this final post about the framework I will discuss the use of a static aspect weaver PostSharp.

Static Aspect Weaving

I assume that you know what aspects are at least at a conceptual level. The nice thing about static aspects is that their code is injected at compile time, not at runtime. True: static aspects are not configurable, but they are completely transparent (dynamic aspects usually require the client code to create a proxy instead of the actual object).

I use PostSharp, a static aspect weaver, to implement a code attribute that triggers aspect code injection into the assembly. PostSharp works at IL level so it should be usable with any .NET language. While PostSharp provides a generic assembly manipulation framework, it is actually Laos that provides the aspect framework.

When an aspect (a code attribute) is applied to a method, PostSharp (actually Laos) rewrites the IL for that method. It basically relocates the original method IL in a new method with the same name, prefixed by a ‘~’. Then it inserts custom IL that performs the call sequence on your aspect object. Your aspect can be responsible for calling the actual method -using a delegate- (as it is in this example), although there are also flavors of aspects that do not require this.

So a typical (simplified) call stack would look something like this:

void MyClass.MyMethod(string param1)
void MyAspect.OnInvocation(MethodInvocationEventArgs eventArgs)
void delegate.DynamicInvoke(object[] parameters)

void MyClass.~MyMethod(string param1)

The stubbed MyClass.MyMethod routes execution to the aspect (OnInvocation) applied to the method and the aspect code invokes the delegate that point to the original method (or it doesn’t 😉 and the original method (prefixed with ~) executes.

TraceContextAspect

In order to eliminate the custom code you’d have to write to use the TraceContext in our method context information gathering framework, I’ve created a PostSharp/Laos aspect class that intercepts the method call as described above. So instead of making all the calls to the TraceContext yourself in the method code, you simply apply the aspect to the method:

[TraceContextAspect]
public string PrintName(int numberOfTimes)
{
    // method impl.
}

The TraceContextAspect implements the OnInvocation method like so:

using (AspectTraceContext ctx = new AspectTraceContext(_methodBase))
{
    ctx.Initialize(eventArgs.Delegate.Target);
    AddParameters(ctx, eventArgs.GetArguments());
    ctx.TraceMethodEntry();

    try
    {
        eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(eventArgs.GetArguments());

        ctx.SetReturnValue(eventArgs.ReturnValue);
    }
    catch (Exception e)
    {
        ctx.AddContextTo(e);
        throw;
    }
} // maintains stack and writes method exit and flushes writer.

Note that I’ve derived a new class from TraceContext for this specific situation (AspectTraceContext) that takes a MethodBase instance as a parameter in its constructor. The MethodBase instance is handed to you by the PostSharp/Laos framework and represent the method the aspect was placed on. The bold text is the actual call to the original method. As you can see, all the custom code needed to setup the TraceContext has now moved to the OnInvocation method implementation.

Conclusion

The use of a static aspect weaver has dramatically simplified the usage of the method context information gathering framework. Tracing useful and rich information from your method has now become a breeze (as it should be ;-).

I hope these last 3 post has shown you how you can leverage existing technology (System.Diagnostics and PostSharp) to make the most out your own tracing framework (in this case). I also hope you will be inspired to find new applications to use static aspects in your own code. I find that static aspects can really make your life easier while at the same time not making your code (execution paths) more complicated than needed.

You can download the source code here.