1

Here's a common question I ask myself:

If I have a ThingDoer class and it has a method DoThing(), and I want to log a message stating "Doing a thing", should I put this logging code in the ThingDoer.DoThing method, or should I keep this code outside of the method, within the caller? I feel like there should be some standard principle to follow here but I don't know what it is.

Log("Doing Thing");
ThingDoer.DoThing();

vs

void ThingDoer::DoThing()
{
    Log("Doing Thing");
}
5
  • 1
    I would say that ideally the logger instance is injected into the ThingDoer, so the caller has some control (should it log at all, or not?), and the callee can take care of logging for itself
    – Alexander
    Feb 19 '21 at 3:10
  • I think the really interesting question is: why is it so hard to find a "standard principle" for this situation? Why do we need more context about the involved classes, and which kind of context?
    – Doc Brown
    Feb 19 '21 at 5:56
  • @DocBrown sheesh, I completely forgot about that answer of mine. Good eye spotting it. Feb 19 '21 at 6:24
  • 2
    @candied_orange your answer seems to suggest that you shouldn't do logging in Java (or any language!) since there's no AOP
    – user253751
    Feb 19 '21 at 9:09
  • @user253751 actually AOP is supported in Java in two different forms. And I'm not saying you shouldn't log. I'm saying it's better if logging code doesn't pollute your class that isn't about logging. Logging is a classic cross cutting concern. Feb 19 '21 at 18:00
0

There is no one-size fits all answer here. Logging tends to go in the location that can give the appropriate details.

For example, if you don't just want to log that your Document is invalid, but you want to log why it is invalid, you're going to want the logging to be done by DocumentValidator itself, rather than the DocumentService which only receives back a boolean from the validator.

On the other hand, if you dig too deep, you lose the context of your call stack. For example, if you really need to know whose document failed, DocumentValidator might not know that, if it only knows how to handle Document objects.
If DocumentService has a reference to a myPerson (who has a Documents property), then the service is the most informed site to write log messages pertaining to both the person and their invalid document.

So it's clear that logging doesn't just belong in one site, it can belong in multiple sites. So how do you implement this? There are several ways of doing it:

  • At a very basic level, you could have a statically available logger that everyone can access.
  • At a more advanced level, you could pass a logger down through the methods, so that all involved dependencies can write their own messages into the same logger.
  • Your objects can define their own logger in their constructor and each write to their own logger.
  • You could rely on DI and an IoC container to inject your loggers for you.

It won't surprise you that there are multiple ways of achieving the same thing, so it's a matter of looking for what suits you best. There are cases where I prefer the ability to pass a single logger between a few dependencies (i.e. so that logger can encapsulate all those messages in a unique location), but it's more common to simply have each class log to their own log.

This depends on the technology you want to use, and the kind of software you're building. I use completely different logging libraries based on the type of project I'm working on.

1
  • 1
    This is very similar to my own thinking here. And again, the usual careless downvoters seem to be around here, who apparently think we all should be able to read their minds why they downvoted this answer. Sure, the question is a duplicate, but the accepted answer to that other question (despite being upvoted by at least 11 members, including me) lacks some practical applicability for many projects.
    – Doc Brown
    Feb 19 '21 at 12:20

Not the answer you're looking for? Browse other questions tagged or ask your own question.