Jonathan Boccara's blog

Open Question: Should the Logging Code Be with the Main Code?

Published April 9, 2021 - 0 Comments

Choosing between two designs is something we do every day as developers, and the choices we make have a large impact on the quality and expressiveness of our code.

Given two designs, how do we make that choice?

Here is a case that I have encountered and where the decision is not obvious, as both options have advantages and drawbacks. After presenting you the two options, I will invite you to express your opinion.

The point here is to practice with evaluating designs. You’ll have the whole comment section with unlimited number of characters (which is why I’m not using Twitter to host this discussion) to express your arguments in favour or against each design, or even to propose a third option. The point is also to benefit from each other’s experience in evaluating designs.

This kind of practice helps evaluate designs and make structural decisions about our code, to make is more robust and expressive.

Logging along the execution?

Suppose you have an existing base of code, and you’d like to add logging to it.

You have a logger object, that has an interface of an output stream resembling the one of std::cout: it has a operator<< to which you can send various types of data.

Each function receives the logger object, and passes it along to the other functions it calls. This way, any function can send data to the logger.

The form of the logged information itself is specific to each function: there is a bit a text describing the operation, embedding data that is specific to this operation. As a result, the code for logging an operation can have a size that is comparable to the one of the main code itself.

The main code of the operation:

// instantiations of objects...
// calling of operations...
// testing of conditions...
// execution of algorithms...

The logging code:

logger << "Execution of operation X, involving : \\n" <<
       << "\\t data1, evaluating to" << data1 << '\n'
       << "\\t data2, evaluating to" << data2 << '\n'
       << "\\t data3, evaluating to" << data3 << '\n'
       << "\\t data4, evaluating to" << data4 << '\n'
       << "it is using the algorithm A, and the point of X is to achieve Y".

This is one possible form of logging code, but in other function its structure can look different. For example, some functions can have several sections of logging corresponding to successive steps of the function.

The question is: should we write the logging code inside the function, or outside of it?

Writing the code outside means that there is a dedicated function for the logging code, that is called by the main code of the operation, or by another call site. This function can be located next to the function with the main code, or all the logging functions can be grouped next to each other.

Note that logging inside of the operation doesn’t create a dependency on a specific logging technology. We use dependency injection in order to pass in an abstract logger.

Advantages and drawbacks

Here are some advantages and drawbacks for each of the two options.

Logging inside the function

If the logging code is written inside the function, one advantage is that the logging code is easier to read, because we can relate it with the main code that is just next to it. In this sense, the logging code becomes more expressive.

But a drawback is that it the function becomes longer overall, and long functions generally hinder expressiveness. This becomes even more noticeable if the logging code is intertwined with the main code.

Also, by embedding logging, the function arguably loses some cohesion: a cohesive function does only one thing, but now it does two: carrying out an operation, and logging.

Logging outside of the function

Logging outside of the function had the converse advantages and drawbacks: without the logging code, the function gets is cohesion back, and is shorter and therefore easier to put in one’s brain. But on the other hand, the logging code becomes disconnected from the main code, and reading it makes less sense.

Another drawback is that if the logging code is separate from the main code, the risk of changing the main logic without updating the logging code is higher. If this happens, then the log displays incorrect information, which is worse than not logging anything at all.

Discussion

Now you know the question: should we write logging code along with the main code? And I have presented a couple of advantages and drawbacks.

But the most important part of this post is to come: it is your thinking about this question, and the discussion in the comments section.

Do you see other advantages or drawbacks? Which solution would you go for, and how did you make the choice? Do you see a third option?

Thinking about these questions gives us practice on evaluating designs. Indeed, some of the arguments you will find in favour or against one of those designs can apply to other design questions in your code. And evaluating designs is one of our main tools to create good code.

The comments section will allow you to formulate your thinking, propose arguments in favour or against a design, propose new designs, and share your perspective with other Fluent C++ readers.

Looking forward to reading your point of view on the question! 👇

You will also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin