I’ve always wanted to have some type of way to get a visual map of the logical flow of a piece of code, especially when that code is not mine. Recently I started working with Aspect Oriented Programming (AOP) which is incredible for reducing lines of code, increasing consistency, saving time and dealing with cross-cut concerns.

What is AOP?

“In computing, aspect-oriented programming (AOP) is a programming paradigm which isolates secondary or supporting functions from the main program’s business logic. It aims to increase modularity by allowing the separation of cross-cutting concerns, forming a basis for aspect-oriented software development.

AOP includes programming methods and tools that support the modularization of concerns at the level of the source code, while “aspect-oriented software development” refers to a whole engineering discipline.” – Wikipedia

Most AOP libraries use MSIL rewriting or injection to achieve the desired results at compile time. Yep, it will change your code. But don’t worry, it isn’t as bad as you might think. It changes your code with code you write. You’ll get a better idea when you see the example below.

The long way

To achieve what we want to do, which is to trace the logical flow of  classes or an entire application, you would have to manually insert Trace.Write() statements at any point you wanted to trace. Let’s say we wanted to know when a method started and ended. We would need to manually add two lines of code to each method for simple methods. If there is complex logic and many exit points to a method, the lines we must add will increase.

public void MyMethod()
        {
            Trace.Write("Start MyMethod");

            //...

            Trace.Write("End MyMethod");

        }

Not only does doing this waste time but it muddles up the code. What happens when you no longer need the trace? You have to manually find and remove the lines. PITA!

The Aspect Way

Using aspects, we can do the same thing easier and faster with no clutter and end up with a more powerful and flexible setup for tracing.

Start a new console application. I’m assuming you already have PostSharp installed and referenced in your project. If not, do so now.

First we will need to create a simple trace listener. We’ll build a StringBuilderTraceListener object. You don’t have to do this, but I did because my needs were specific. Feel free to use any already available trace listener.


public class StringBuilderTraceListener : TraceListener
 {
 private StringBuilder _trace;
 private string _category;

 public StringBuilderTraceListener() : this(string.Empty) { }

 public StringBuilderTraceListener(string category)
 {
 _trace = new StringBuilder();
 _category = category;
 }

 public override void Write(string message)
 {
 Trace(_category, message);
 }

 public override void WriteLine(string message)
 {
 Trace(_category, message);
 }

 public string GetTrace()
 {
 return _trace.ToString();
 }

 }

Now for the aspect


[Serializable]
 internal class MethodTrace : OnMethodBoundaryAspect
 {

 private string _category;

 public MethodTrace(string Category)
 {
 _category = Category;
 }

 public override void OnEntry(MethodExecutionArgs args)
 {
 Trace.WriteLine(string.Format("{0}: Started", args.Method.Name), _category);
 }

 public override void OnSuccess(MethodExecutionArgs args)
 {
 Trace.WriteLine(string.Format("{0}: Finished", args.Method.Name), _category);
 }

 public override void OnException(MethodExecutionArgs args)
 {
 Trace.WriteLine(string.Format("{0}: Failed due to exception\n{1}", args.Method.Name, args.Exception.Message), _category);
 }
 }

We’re using an OnMethodBoundryAspect type attribute to give us access to the various stages of our methods. We simply fill in the blanks with our tracing code and we’re done.

And the dummy class


[MethodTrace("MyClass")]
 public class MyClass
 {
 public MyClass()
 {

 }

 public void Method1()
 {
 Method2();
 }

 public void Method2()
 {
try
{
Method3();
}
catch
{

}
 }

 public void Method3()
 {
throw new InvalidOperationException("");
 }
 }

Very basic, just for testing. Notice that we added the attribute to the class, not individual methods. You can add to each method if you want to be selective but adding it to the class level will automatically apply the aspect to all methods (public and private). This can also be applied at the assembly level but you can figure that out on your own.

Now in the Main(), let’s add our trace listener and create our class with some calls to it.


StringBuilderTraceListener listener = new StringBuilderTraceListener("MyTrace");

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

MyClass obj = new MyClass();

obj.Method1();

Console.Write(listener.GetTrace());

Console.ReadyKey();

The result is:

: MyClass: .ctor: Started
: MyClass: .ctor: Finished
: MyClass: Method1: Started
: MyClass: Method2: Started
: MyClass: Method3: Started
: MyClass: Method3: Failed due to exception
: MyClass: Method2: Finished
: MyClass: Method1: Finished

There you have it. With the PostSharp library, you also have access to method arguments and many other facets so you can increase what gets added to your trace.

Advertisements