Stelios (3) [Avatar] Offline
#1
Regarding Ambient context the first thing I've noticed is that while it was mentioned as a pattern in the 1st edition now it's an anti-pattern. That certainly worth a brief explanation.

Regarding Ambient Context itself, my experience for the very same example that you demonstrate (Logging) was quite the opposite: Initially I've implemented a poor mans logging. I had a Logger static class and wherever I wanted to log I was calling Logger.Write("message"); It worked fine for a long time, with its quirks as it was not carefully implemented but it served me well as a quick logging solution. Until I though it was good idea to move towards a logging library. I chose NLog and I only had to change the Logger static class which effectively became an adapter for the NLog interface. It was easier than I imagined and the transition came quicker than I anticipated. I don't expect to change logging libraries every day after all smilie

The difference in my implementation is that instead of doing
    private static readonly ILog Logger = LogManager.GetLogger(typeof(MessageGenerator));
	...
	Logger.Info("GetWelcomeMessage called.");


I did
	LogManager.Info("GetWelcomeMessage called.");


So my LogManager is implemented along the lines of .NET's Environment class. That way I save my self from an unnecessary dependency on ILog interface and I can change LogManager Info method as it pleases me.

Having the above example in mind, I don't see how the ambient context can be bad (at least the way I've implemented it) or in other words the book's example of logging doesn't convince me that ambient context is an anti-pattern.

Regards,
Stelios
Steven van Deursen (34) [Avatar] Offline
#2
Hi Stelios,

I'm sorry we couldn't convince you on this. We've given all the arguments on why it is a bad idea in the book, so I won't be repeating it again here.

You probably read the first edition a long time ago, but even in the first edition, you can read between the lines that, at that time, Mark already thought that the use of Ambient Context should be really rare, and that in general there are better solutions.

Over the years, Mark and I both came to the conclusion that there are _always_ better solutions, which is why we decided to explicitly state Ambient Context as an anti-pattern. We realized, however, that there was the risk of offending people, since this is a _very_ common pattern. That's why chapter 5 gives two examples of widespread Ambient Context use, while describing better alternatives. Whether you take our advice or not, is completely up to you.
Stelios (3) [Avatar] Offline
#3
Hi Steven and thanks for replying so fast.

I think you have taken me wrong though! Please don't be sorry for anything. It's just my nature to challenge the things that I read. Also I'm not offended. Other people might think that when you challenge their technique you step into the lawn; I'm not one of them. I wouldn't be buying the book if I didn't want to improve and hear what someone else has to say.

What I'm trying to say is that there are valid counter-arguments of the ambient context: logging is an exceptional case. It's cross cutting concern. You might need it anywhere and it's a hassle (In My Humble Opinion always) to pass an ILog (or however you want to call it) instance on every object that needs to write a couple of lines in the logs.

What I want to challenge with my example is this: (I quote from the book)
More importantly was that even though the application designers hid the use of log4net behind an Abstraction, there was still a Dependency on a third-party library and every class depended on the Ambient Context provided by Common Logging, much like listing 5.13 shows.

Emphasis is mine. What I demonstrated is that it doesn't have to be like that.

Also I didn't mention anything about the time example. I couldn't agree more on that. So as I see it, ambient context is not an antipattern, it just has to be used wisely and sparingly. (IMHO again)

I wanted my post to be a constructive feedback for once, instead of pointing syntax errors of the book.

Thanks again for the response and keep up the good work.
Steven van Deursen (34) [Avatar] Offline
#4

logging is an exceptional case. It's cross cutting concern.


I would argue that logging is -not- an exceptional case. There are many Volatile Dependencies in our system and we have to deal with Cross-Cutting Concerns all the time. What makes this particular Cross-Cutting Concern so different and exceptional to all other Dependencies and Cross-Cutting Concerns that we accept the downsides that Ambient Context brings?

Is it perhaps that we need it all over the place -or- at least we think we do?


it's a hassle to pass an ILog instance on every object that needs to write a couple of lines in the logs.


That's great, because it -should- be a hassle. That means your design is telling you something. The hard part however is figuring out what exactly it is telling you. But my gut feeling is that you log at too many places in your application.

We addressed this in this callout:


IMPORTANT By no means we are stating that you shouldn’t log. Logging is a crucial part for any application, as it is in the applications we build. What we are saying however is that you should design your application in such way that you only a handful of classes in your system are affected by logging.


But now the question obviously becomes, how exactly should you design your system in such way that you can minimize the amount of log statements? There are many answers to this, but as we hinted in section 5.3, chapter 10 goes into great length to demonstrate an application design that can help with these kinds of issues.

Did you already read chapter 10?
Stelios (3) [Avatar] Offline
#5
What makes this particular Cross-Cutting Concern so different and exceptional

Oh yes, let me elaborate on that; I have to support it with arguments:

* It's like a disease: it can happen to anyone (any class or method) and you cannot predict who that is.
* It's very unlikely to change. At least I wouldn't change something in logging unless I had good reason to do so.
* It's quite simple: timestamp, message level and if it exists a stack trace. These specs are quite hard to change.
* One might argue that "there is no such thing as too much logging": there has to be a level of verbosity from "errors only" to "spam me mercilessly". It's when you have bugs in production where you need to turn on verbose logging.
* You don't want to test it. Yes I know. You should test it you will say. But wherever I worked for the past 8 years, I was wishing that logging wasn't the only thing covered by unit tests. Usually it was the rest of the code as well.

The above I think are unique to logging and that's why it's "special". I couldn't find any similarities to other Aspects. At least not obvious.

Did you already read chapter 10?

Indeed but it was so long ago that I had to actually go back and see what was chapter 10 about. My (limited) experience with decorators is that not everything can be a command and thus be decorated. But let me make a (long) pause here as I have quite recently started refactoring my application to implement aspects using decorators. As a result I don't have yet the experience to give valid feedback.
Steven van Deursen (34) [Avatar] Offline
#6
Whether a Volatile Dependency is simple or not, does not change the fact that it still is a Volatile Dependency. And although some might argue that there is no such thing as too much logging, I would argue the other way around. The systems I've helped design the last say 8 years never had lines of logging code spread out through the vast majority of classes in the system. We did log, and we were able to log more by increasing the verbosity level, but we typically had a handful of classes in the system that actually did the logging. So the most important point here is: you might want to log a lot, but that doesn’t mean you should have logging statements spread out through the system “like a disease”.

Here are some pointers that I always watch out for:

  • - Smell: Having method entry and exit logging statements. (Probable cause: missing good abstractions, see chapter 10)

  • - Smell: Logging in places that should have been exceptions (probable cause: not truly understanding the domain, or being afraid to throw)

  • - Smell: Logging exceptions and re-throwing them (probable cause: missing top-level catch that logs errors)


  • I like to, again, emphasize that a good design, for the most part, prevents logging from spreading "like a disease". Chapter 10 provides some clues on what type of design can help in preventing cross-cutting concerns like logging from spreading like a virus. Chapter 10 is not a complete answer to this, however. For instance, it indeed doesn't explain how to decorate other parts of the systems (i.e., your queries), but the chapter does reference a blog post that actually does describe this. The chapter is not meant to describe a full end-to-end solution to design your complete application with. That by itself would take a complete book to describe. The chapter, however, tries to give you an idea of how to model your system, and what good abstractions would be.

    The book, however, describes other (related) patterns that help in increasing maintainability. One of these patterns is Domain Events (see section 6.1.3), additionally with the use of durable queuing (see the last sidebar of section 6.1). Domain Events allow for an even more fine grained design, which gives even more options in pulling logging into the application’s infrastructure (e.g. using decorators), and even allows the handling of events to be scheduled, queued, retried, and… logged.

    This story, however, might be very uncomfortable, because, most likely, fixing the disease-like design is a big design change, which is likely to be a lot of work. And it’s not only a matter of design, but coding practices and habits as well, which is probably just as hard to change.

    But this is obviously how things work: new insights happen all the time, and you will have to determine whether this change is feasible for your application at this point in time. But as we stated at the introduction of chapter 5:


    In some cases, an anti-pattern might be the most appropriate _temporary_ solution. Even though the application of an anti-pattern might be an improvement over the original code, it is important to note that this doesn't make it any less an anti-pattern, because another documented and repeatable solution exists that is proven to be more effective.