Aspect-oriented logging for .NET with Unity and SmartInspect

This article is part of a series about aspect-oriented logging with SmartInspect, our logging tool for .NET, Java and Delphi. Please see the first article of this series for a complete overview of the available posts.

Michael Baarz recently emailed us as he was a looking for a modern .NET logging tool for his company’s complex telecommunication and PBX software. Michael was about to redesign his company’s flagship product and was planning to use Microsoft’s dependency injection framework Unity.

He told us that he really enjoyed working with SmartInspect and because Unity also supports some aspect oriented programming features, Michael decided to write an unofficial library to integrate SmartInspect with Unity so he could benefit from this useful combination. This posting introduces Michael’s library and will explain how to get started with the extension to integrate and benefit from SmartInspect and Unity.

Unity, a dependency injection container

Unity is Microsoft’s dependency injection container and framework and is part of their Patterns & Practices initiative. If you are new to dependency injection in general, I recommend taking a look at this article by Martin Fowler that explains the basic concepts and benefits. Besides common dependency injection features, Unity also supports aspect oriented programming by allowing you to intercept and wrap method calls.

The big difference between dependency injection (DI) frameworks and classic AOP tools such as PostSharp is that DI frameworks are configured at run-time, whereas PostSharp injects and builds its aspects at compile time. Both approaches have their pros and cons, so it really depends on the circumstances which one you should use (DI frameworks are usually more flexible as you can change the behavior at run-time, whereas ‘classic’ AOP tools support more features and are usually faster).

Using the SmartInspect Unity Interception Extension

Michael’s library to integrate SmartInspect and Unity is called the SmartInspect Unity Interception Extension and can be downloaded from CodePlex. To get started with the library, either check out the source code via Subversion or download one of the releases (there wasn’t a release at the time of this writing yet).

To use the library in your solution, you first need to add a few assembly references to your projects. First of all, you need to reference the Microsoft.Practices.Unity and Microsoft.Practices.Unity.Interception assemblies; these are part of Unity and are automatically installed and registered when you download and install Unity. The next assembly reference you need to add is Gurock.SmartInspect, as this is required to use SmartInspect logging. Last but not least you need to add Michael’s SmartInspect.Extensions.Unity assembly as a reference to your project, either by adding a reference to the compiled DLL or by adding the project to your solution and using a project reference.

Tracing method execution

One of the more useful aspects (no pun intended) of AOP is the capability to easily trace method execution, so this is the first thing I’m going to demonstrate. Unity is a dependency injection framework so we will need to properly register and instantiate all classes we want to intercept for logging. The first thing we need to do is to define the interface for the class we want to instantiate through Unity. Because one of the design goals of DI is that we can easily replace the actual implementation for an interface, using interfaces is mandatory here. For our Hello, World example, I’ve defined an interface and implementation class:

[sourcecode language=”csharp”]
interface IHello
{
int Count { get; set; }
void SayHello(string name);
string SayGoodbye(string name);
void RaiseException(string name);
}
[/sourcecode]

The actual implementation already makes use of SmartInspect to log various messages:

[sourcecode language=”csharp”]
class Hello : IHello
{
public int Count { get; set; }

public void SayHello(string name)
{
SiAuto.Main.LogMessage("Hello, {0}.", name);
}

public string SayGoodbye(string name)
{
string msg = String.Format("Goodbye, {0}.", name);
SiAuto.Main.LogMessage(msg);
return msg;
}

public void RaiseException(string name)
{
SiAuto.Main.LogMessage("Hello, {0}.", name);
throw new Exception("This is a test exception");
}
}
[/sourcecode]

Now to intercept and trace the method calls, we first need to make sure that we register and instantiate the interface and implementation with Unity. To do this, we use an instance of UnityContainer and add and configure the Interception extension. Here’s the full Main method of our example program:

[sourcecode language=”csharp”]
static void Main(string[] args)
{
// Enable SmartInspect logging
SiAuto.Si.Enabled = true;

// Create the Unity dependency injection container,
// register the Interception extension and register our
// our example class
IUnityContainer container = new UnityContainer();
container
.AddNewExtension<Interception>()
.RegisterType<IHello, Hello>();

// Now tell Unity how to intercept IHello method calls
container.Configure<Interception>()
.SetInterceptorFor<IHello>(new TransparentProxyInterceptor());

// Instead of creating our Hello class directly, we ask
// Unity to resolve the dependency for the IHello interface
IHello hello = container.Resolve<IHello>();

// Call The IHello methods and assign property values
hello.SayHello("World");
hello.SayGoodbye("World");

Random r = new Random();
for (int i = 0; i < 10; i++)
{
hello.Count = r.Next(100);
Thread.Sleep(1000);
}

hello.RaiseException("Hello World");
}
[/sourcecode]

Now to actually use the the SmartInspect Unity extension and trace method calls, we just need to add a single attribute to the interface, namely the [SiMethodCall] attribute (which can be found in the SmartInspect.Extensions.Unity namespace):

[sourcecode language=”csharp”]
[SiMethodCall]
interface IHello
{
// [..]
}
[/sourcecode]

The attributes come with various properties to influence the behavior of the log calls, such as changing the log level, adding name prefixes/suffixes and so on. For a complete list of properties just see the Intellisense help within Visual Studio. Once we launch the example application, we see the following output in the SmartInspect Console (also take a look at the viewer toolbox at the bottom right of the window, it shows the method arguments of the selected log entry):


Log messages and method tracing in the SmartInspect Console

Logging exceptions

Logging occurred exceptions is another useful thing you can automate with AOP. The SmartInspect Unity extension comes with its own attribute to log exceptions, namely the [SiException] attribute. The attribute doesn’t handle the exceptions (it just logs them) so it doesn’t change the behavior of your code. To use the attribute, just specify it for an interface or method like this:

[sourcecode language=”csharp”]
[SiException(LogArgumentsToViewer = true)]
interface IHello
{
// [..]
}
[/sourcecode]

The resulting log messages in the SmartInspect Console look like this:


Logged exception in the SmartInspect Console

Watching property values

SmartInspect allows you to easily log and watch field and property values. The SmartInspect Unity extension comes with an attribute to automatically log changes of properties as they occur. To benefit from this, simply add the [SiWatch] attribute to the set method of your properties:

[sourcecode language=”csharp”]
interface IHello
{
int Count { get; [SiWatch]set; }
}
[/sourcecode]

This blog posting is intended to give you an overview of the capabilities the SmartInspect Unity extension library provides, but it cannot explain all the useful options and features that the library comes with. So to get started with the library and learn about all its capabilities, I recommend downloading it and start playing with it.

Please also note that the SmartInspect Unity extension introduced here is not a Gurock Software product and is not official supported by us. To send feedback and to contribute to the project, please take a look at the CodePlex project page. Thanks Michael for writing the library and for contributing to the SmartInspect ecosystem!