7

What are the pros and cons to each of the following ways of logging a function call? This code is written in Ruby but I feel the question applies to programming in general.

Responsibility belongs to the callee

class Foo
  def do_cool_stuff
    # Do some cool stuff
    Logger.info("I did some cool stuff.")
  end
end

class Bar
  def self.trigger_cool_stuff
    foo = Foo.new
    foo.do_cool_stuff
  end
end

Bar.trigger_cool_stuff

Responsibility belongs to the caller

class Foo
  def do_cool_stuff
    # Do some cool stuff
  end
end

class Bar
  def self.trigger_cool_stuff
    foo = Foo.new
    foo.do_cool_stuff
    Logger.info("I did some cool stuff.")
  end
end

Bar.trigger_cool_stuff

My initial thought is that it's a better idea for the callee to log its own actions, because it's just clearer that your logs are coming from the bit of code that is actually performing the actions that you're logging about in the first place. However, it does seem to violate Single Responsibility Principle. Additionally, if I wanted to control some state regarding the logs, like whether or not the log happens at all, the callee now must also be aware of that, which also violates SRP.

Is there a generally accepted way of doing this, or is it more down to a matter of opinion/preference?

  • 1
    If the caller has to add info to the log trace that doesn't belong to it, we'll be forced to add accessors in the callee. To me, that's wrong. Each component leaves log traces with the info (state, parameters, etc) it holds and delegates the log traces of further components to the components themselves. Otherwise, enabling and disabling appenders is useless and the logs become harder to interpret because everything happens in the same few components. – Laiv Jul 25 at 7:20
9

Logging doesn't care. Everything is both a caller and a callee. Nothing runs without being called. Nothing gets anything done without calling something.

Logging only cares about what was done. Not how you looked at it.

In Aspect Oriented Programming (AOP), neither take responsibility for logging. Logging is taken over by point cut code that wraps your method with code that does the logging. This lets the rest of the code be blissfully unaware that logging is even happening.

When you put logging anywhere that isn't just about logging you're mucking up SRP. But not everyone has an AOP solution. So we do the best we can.

3

Logs without any extra information can be logged anywhere. There is no rule about it. It's totally up to you. But, sometimes, we need to add some extra information to log that caller can't know. So, methods, which calls from caller, may log it inside.

CallerMethod(customerNumber)
{
   method1(customerNumber);
   method2();

   Logger.Log("I did my business");

}

method1(customerNumber)
{
    var customer = findCustomer(customerNumber);
    int orderNumber = doBusiness();

    // State, FullName and orderNumber informations don't exist on caller method.
    Logger.Log(string.Format("Business is done for customer {0} from {1} by order number {2}", customer.FullName, customer.State, orderNumber));
}

Thus, I think, callee methods are more proper place to log something for its business.

And, just calling another method in a method doesn't mean violating SPR. You just call, not doing logging operations.

2

In the caller: You have to log at every caller place where you are interested in logging. In the callee: Only one logging statement but every call is logged.

You pick what you want. You trade flexibility for more work.

1

What exactly is to be logged should also be considered. In case of a large module with multiple nested method calls logging might be beneficial inside the callee method if any specific details are to be captured in the logs; whereas a caller might only be able to log the status of the call(s) made to the callee.

Specifically, in cases where exception handling is involved it is rather beneficial to log inside the callee method itself if the exception is caught instead of being thrown.

1

There are basically two ways to do this neatly, the more common one (just my impression) being to inject a logger interface into each and every object you may expect to log something at some point. SRP is not violated because the logging itself is delegated, what you call the callee is just calling a method on the logger interface. The callee is not responsible for logging in the sense that it is not handling/implementing it, it is just using it. So regarding your question, you might as well log at the point where the message is relevant since the logger interface is omni-present anyway.

I am not a fan of this model. The logging interface will be everywhere and so will logging statements. Logging is just one way for an object to notify the world about something. So if I get to design stuff from scratch I implement notification events on my classes and leave it op to the user of the class to either subscribe or ignore. This is more plumbing but you win on decoupling and usability. The class does not have to know about logging at all. You can centralize logging (and route notifications to any other channel for that matter) on a high level in your application. You have more control over presentation and destination of your notifications. This is the purer way to handle it from an OO perspective. But again, it is more work and apparently to many not worth the trouble.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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