Configure NLog with StructureMap

In a couple of recent posts, I demonstrated how to set up StructureMap 4 in a simple .NET console application, and how to configure NLog to capture and record some app-specific fields with each logged entry (well, to be able to do so if desired, anyway). If you’re using StructureMap already in your application, you may want to leverage it for your logging as well. Let’s consider that, and then show a couple of ways to do it that keep your code DRY.

Logging as an Abstraction

There is some debate about where logging logic belongs in your application, especially if you are following Domain-Driven Design. Logging is a cross-cutting concern, and not necessarily one that the core domain should worry about. However, it’s quite likely that you will want to perform some logging in your core domain. Assuming you’ve separated your application’s infrastructure from its core (learn more), this means either you need to put implementation details about your logging in your core project, or you need some kind of ILogger abstraction that is implemented in your infrastructure. Usually I lean toward the latter, since I try to follow the Dependency Inversion Principle, which suggests that high level modules (i.e. domain model) should not depend on low level modules (i.e. a logger implementation). However, there are also those who argue against using dependency injection with loggers, and they make some good points. Most importantly, that the typical ILogger facade is going to leave out a lot of the best features of your logger, and in any case logging code shouldn’t impact your ability to run tests (if it does, you’ve configured your logger wrong in your test project).

I’m going to take a middle road for this article, which is that we can work with a concrete Logger class (and thus have full access to its goodness), but still use dependency injection to make the logger available to our classes (thus eliminating some boiler-plate code in every class that might require a logger). In order to inject the logger into classes that need it, we need to decide whether we should use constructor or property injection. In the former case, we have no choice but to include it in the list of constructor arguments, which helps us to follow the Explicit Dependencies Principle, but adds a little bit more code we need to write. The second case, property injection, would allow us to place the Logger property in a base class. Once in place, any child of this base class would “just work” without additional code.

In both cases, one of the nice features of NLog is its ability to use named loggers, which typically correspond to the fully qualified class name of the class they’re associated with. The techniques below will not require us to sacrifice this functionality.

Inject Logger as Property

In order to inject the logger as a property, we first need a base class that exposes the property. One disadvantage of this approach is that this property, which should only ever be called from within the class itself, is exposed (at least partially) as part of the class’s interface.

Classes that inherit from LoggedClass can use the logger as expected:

The property is populated automatically by StructureMap using a custom policy. This policy checks to see if there is a property called Logger, and if there is, it creates a logger instance and sets the property to this instance. Otherwise, it does nothing. The reason we’re using a custom policy is to give us access to the pluginType so that the logger is properly named for the type it is being injected into (and not, for instance, ConsoleRegistry where StructureMap is creating it).

The policy is then added to StructureMap in the registry class that is used to set it up:

Inject Logger via Constructor

Unfortunately, we can’t just create a base class, give it a constructor that accepts an instance of Logger, and be done with it. Nor will an interface help us achieve any reuse in this case – the logger isn’t something we want as part of our public interface. Thus, we’ll need to request an instance of the logger anywhere we want to perform some logging. But this is what we do anyway if we’re writing SOLID, DDD-style code. Here’s what a type requiring logging looks like:

As above, we could at this point use a standard StructureMap rule to provide an instance of a Logger to the class, but it wouldn’t get the name of the class properly. Instead, we use a similar policy-based approach, targeting constructor parameters rather than properties:

With this in place, the ConsoleRegistry can add it to its Policies collection:

Summary

Which approach do you prefer? There are plenty of opinions on this subject that you’ll find with a quick search. My current preference is with the constructor injection approach, but using NLog instances directly rather than a custom ILogger facade. I’ve demonstrated that I can test the code that includes the loggers without any problem, and if I need to actually test that some logging is performed correctly, I have a way to inject my own custom logger implementation or mock that would let me detect this.

  • Jos Krause

    Thanks Steve, I always wondered about specifying an extension method for the logging abstraction library/namespace and then using that to provide logging where you need it.

    e.g. public static ILogger Log(this T obj) where T : class;

    Then in your code you’d do:

    this.Log().Info(“etc”);

    Following your article; returning a Logger instance rather than the interface would then also perfectly be possible and fine. Since we have multiple ways to log the interface is used for our particular purpose.

    You’d end up with no additional Ctor clutter, nor having to maintain a property or exposing this dependency on public interfaces, though I am sure this approach is not without caveats and apprehension, I wouldn’t mind if you could share your opinion on this approach?

    • ardalis

      You would put that static ILogger Log on every class that required logging? That would probably be the first issue I might have with the approach – the repetition of it. Then, I would want to ensure that the static calls didn’t impede testing, but as long as you can configure your test project to make all logging calls no-ops, that should be fine.