On this particular aspect - I find that log lines can do double-duty as code comments. When I see some comment that explains the what-and-why of a section of code - "// We also need to check the Foo state for consistency before committing Bar" - I might actually change it to a log line like - "Verifying state of Foo ${foo.id} before committing Bar ${bar.id}".
The article says to use logging and not actual printf debugging, which solves this. If your logging framework is even somewhat capable and modern it understands all of:
- named priorities: I want that FATAL log highlighted, but I probably don't want the DEBUG log shown at all unless I am currently debugging this
- automatic categories: os.mutex.internal and account.forex.hack can be automatically distinguished in most modern languages by the fact they live in different objects / source code files / even in C we know what the name of the current function is and can at least annotate that in logs.
- how to send the log messages somewhere useful, hopefully at least files and some primitive syslog type protocol, maybe a lot more
In a nice shiny modern logging framework I'd expect to be provided with default annotations so that e.g. when I error.log("Can't enrol students on a module that doesn't exist") the framework annotates the log entry with: The HTTP request that got us here, including e.g. query parameters and date-time stamp, and the Module we were trying to call a method on when it blew up.
But I haven't found it very useful to barf lots of data into a debug log and then hope to find something useful sifting through it when there's a problem. Maybe it's just one too many information-dense things to juggle for me and smarter people have more luck with this approach. I'm not totally against the idea, I just haven't found a way that works for me.
I find that having lots of trace data is useful because at a glance, you can compare problem situations against normal profiles and patterns.
When I fix a problem gnarly enough to require lots of logging everywhere, once I've tidied up I write a detailed comment or other documentation, and/or use unit tests to make sure the problem stays solved. The logging code doesn't get committed, which also helps keep the fixed code clear in the diff.
For the kinds of programs I usually write, I've settled on only using printing/logging for major events and for things that aren't errors but might require developer attention.