Logging, Aspect Oriented Programming, and Dependency Injection - Trying to make sense of it all
Asked Answered
A

1

39

I know that logging is a prime use case for AOP. Additionally logging wrappers are also exemplified as cases when you want to use DI so that classes aren't coupled with a specific logging implementation. However, some consider logging wrappers an anti-pattern. Primarily, such a view is because in most cases the wrapper tends to be simplistic and removes many of the features specific of the logging framework. If you implement those specific features, why not just use the framework directly.

I am aware of the Common.Logging facade that attempts to abstract a good amount of the features of log4Net, EntLib, NLog for you. However, even here we still have a dependency of sorts on Common.Logging. Not in a code/unit testing way regarding interfaces and such, but if the project dies (it's been over a year since the last release) or you want to latter switch to a logger not supported, that can cause problems.

That said, if logging is achieved via AOP, is it even necessary to use DI for the logging dependency (i.e. why not just directly reference say NLog)? Yes that AOP portion of code would be tightly coupled, but the logic of the classes that one wants to unit test is devoid of logging dependencies (at least before the weaving happens). It's at this point that I'm a bit lost (I haven't tried AOP yet). After weaving, would having not used DI for the AOP code cause problems for unit testing the method under test? Or can one unit test without weaving the AOP code?

Unless logging is a requirement of the user of the software, I'm not sure how useful it is to test that logging has occured with mocks. I would think that the business logic of the method under test is what most would be interested in testing. Lastly, if one wants to use TDD/BDD, wouldn't one have to use DI for the logging dependencies in the AOP code? Or would one just not test drive the AOP side of things?

As you can see, I'm trying to get a feel for what the most practical approach is for developing an application that would be using both AOP for cross-cutting-concerns and DI for design/testing. Since AOP is relatively new, and logging is the most common example, what is the recommended approach?

Aweinspiring answered 26/10, 2011 at 15:36 Comment(1)
Related: #9892637Pentastich
M
63

Logging is not a Service, it's a cross-cutting concern. As such, it's best implemented with a Decorator. However, adding lots of Decorators just to enable logging of various different services tend to violate DRY, in which case you can then further evolve those Decorators into a single Interceptor.

While you can use IL weaving to implement AOP, a better option is to use a DI Container that supports dynamic interception, as it's a far more lightweight solution.

This enables you to completely decouple concrete services from logging. In that case then, I'd say that there's no reason to wrap any particular logging framework, because if you ever want to change the logging framework, you can just change that single Interceptor.

Here's an example that talks about Decorators and Interceptors for instrumentation (very similar to logging).

If you want to learn more about AOP and DI, you can view online this talk I gave at GOTO Copenhagen 2010.

Masuria answered 26/10, 2011 at 17:33 Comment(11)
I was hoping you would respond to this Mark. I've found your insight into pretty much everything related to DI very helpful, as well as unit testing (automocking, etc). I'll take a look at your example and talk. A side question: Are there any situations where AOP is preferable over dynamic interception?Aweinspiring
Dynamic Interception is also AOP :) However, I interpret your question as relating to AOP via IL Weaving. In that case, on a greenfield project, I see absolutely no benefit from it, but on legacy code it may be practical because it allows you to apply aspects to static, internal and/or private types and members, whereas dynamic interception requires that you're programming against interfaces (or base classes).Masuria
Disagree with decorator pattern. Because it makes all classes in your assembly decorated with loggers. A class implementing IProcessor will have a corresponding logger which implements IProcessor to enable decoration.Narvaez
@Vijay Each class implementing an interface may have a decorating logger - if it's required. It's completely optional. What is the problem with that?Masuria
@Mark, I have detailed an answer to this question in a post: vijayt.com/Post/Guide-to-implement-cross-cutting-concerns. Decorator pattern is useful if the decorator is DynamicObject. In this case, we have a single decorator for all interfaces. In some class methods, loggers are required between method calls - not just before and after a call. For this, dependency injection is best suited.Narvaez
@Vijay If you need to to log between method calls, could it an indication that the method does too much (violates SRP)?Masuria
@mark-seemann "If you need to to log between method calls, could it an indication that the method does too much (violates SRP)?" This is a very smart observation. It reminded me of An Empty Line Is A Code SmellBlew
@MarkSeemann: In case I'm sure the method doesn't do too much and I still need to log between method calls, I don't see any other choice than to inject the logger. Or do you know a better solution? The case I'm thinking about right now is something like this: if (OptionalConfigValue == null) { log.Warn("..."); }Pierson
@ChristianSpecht Please ask a new question, and I'll be happy to take a look.Masuria
@MarkSeemann: You're right. Here it is!Pierson
Unfortunately, the Decorator pattern doesn't help with private methods.Begin

© 2022 - 2024 — McMap. All rights reserved.