Logging is an essential part of every application. Might be dead simple as Console.WriteLine() or a complex third party library but every piece of software needs a way to communicate its status.
In my last project I have decided to use the wonderful NLog . One of the good things this library has is the possibility to add some contextual information to the messages, including (by default) the name of the calling class. Something like this:
2017-10-06 17:01:06.0417|INFO|MyAwesomeProgram.MyAwesomeClass|This is my message
As you can see, I have outlined with different colors the level, the name of the caller and the text.
This can be easily accomplished by something like this:
Using a static cTor to init the logger helps to get a better stack trace in case GetCurrentClassLogger() throws an exception for some reason.
However, in case you’re using a DI container (as you should), it may become complicate to inject a valid Logger instance into each class.
Getting back to my project, for this I am using StructureMap as DI container. Usually I tend to stick with Simple Injector, but I just joined the team and it’s very important to follow the existing conventions.
The idea here is to use a wrapper class around the logger instance that takes as cTor parameter the name of the calling class. I’m using a wrapper just to avoid coupling with a third party library: usually you wouldn’t change from a logging library to another unless you’ve a very good reason for, but this principle applies basically to anything.
Next step is to configure StructureMap to return each time a new instance of the LoggerWrapper with the proper calling class name. And that’s actually the easy part!
Here’s the full code:
As you can see the magic happens during the StructureMap setup: context.ParentType represents the type of the class that will receive the LoggerWrapper instance.
Now let’s take a step back. Logging falls into the category of “cross-cutting concerns” , which basically means that logging can be considered part of the “infrastructure” and of course not of the business logic of the application. Caching can be another good example.
That said, unless you really need to write specific log messages in specific points of your application, another option could be using the Decorator pattern . The idea is pretty straightforward:
- create a FooLoggingDecorator class
- inject the logger
- wrap all the class methods
- add logging where needed
Now it’s up to you to decide which approach to take, they both have pros and cons. For example, injecting the logger in some case can imply a SRP violation. On the other hand, using a decorator requires wrapping all the inner class methods. Keep that in mind when you add stuff to your interfaces 🙂
As Jeremy wrote in his comment, the StructureMap documentation suggests to use a logger convention instead which would be (quoting):
“significantly more efficient at runtime because the decision about which
Loggerto use is only done once upfront”