An Aspect-Oriented Programming (AOP) Approach to Logging

New: Dynamically evaluate C# expressions and execute C# scripts with a single statement, from anywhere in a .NET application. Click here for more info.

Logging is a topic near and dear to my heart, having in an earlier version of .NET created a logging package tuned for high performance and used many others since. Today, with multiple popular offerings available to the .NET developer with different strengths and weaknesses (NLog, Log4Net, Serilog et al.) it’s not unusual to see adapters in local codebases to allow configuration and use of different packages as desired. This is actually a practice I recommend, to decouple local APIs from the implementation of a solution to a common and cross-cutting concern.

This naturally leads to thoughts of simplifying access in one’s API to the logging code. Aspect-oriented logging has in the past included attribute-based approaches, such as in PostSharp. But what if one hasn’t adopted such a library, or would like to log statements from code inside existing methods?

Suppose that one has logging classes presumably configured using some IoC implementation, and wants to decorate an API with logging functionality without undue clutter. One can use C# extension methods and weak references together with a marker interface to achieve the desired effect. Here are the steps:

1. Create an interface with which to decorate classes that will generate log information. (In the linked code sample, see the ILogSource interface.)

2. Add extensions to the log-source interface to support logging messages and/or events, corresponding with the desired use of the logging API, and to get and set a logger using a weak reference. (In the linked code sample, see the ILogSourceExtensions static class, stored with ILogSource in ILogSource.cs).

3. Decorate any desired class with logging functionality by implementing the marker interface, and configuring it with a logger as desired, then calling its logging methods within its other code. (See the code sample for more.)

This approach still allows an adapter to a target logging API to be used, and run-time configuration of the implementation as desired. It merely provides syntactic sugar to avoid littering your API with logging substructure in base classes and the like, by reducing the necessary plumbing to a single interface marked on the logging client class. The overhead of looking up the weak-referenced logger turns out to be minimal, at several nanoseconds per call. I’m still thinking through how best to wire this together with injection; constructor injection seems to obviously be out of the question.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s