My software is in layers, with a well-defined API between each layer. I tend to implement logging for those APIs from the begining, so that for any given transaction I can see how that results in an API call at each of the underlying layers: if there's a mistake, that will help to narrow it down to a specific layer. The logging will also help to reproduce a problem by showing a log of all the previous, normal activity which led up to the problem.
I also add assertions within each layer where I can, and log assertion failures.
A log file therefore often shows of a sequence of calls to the public APIs, with an error message generated from inside: that's often enough to diagnose the problem.
Beyond that, I add debug-level logging on an as-needed basis: to debug specific problems during development and/or after release.
My reasoning for caring about logging is partly explained in the following:
To fix any bug in released software, I depend on the log file; and the same is also often true of software as it's being developed.
You said,
I find I rarely decorate any lower level classes such as implementation of ICakeRepository with logger stuff as it seems pointless.
I said,
My software is in layers, with a well-defined API between each layer. I tend to implement logging for those APIs from the begining ...
I think I'd better explain what I mean by "layers", which may or may not be the same as your "lower level" classes.
For example, my system might have the following layers:
- UI
- Business layer (rules for manipulating data)
- Data access layer (for database I/O)
- Database
In that case I would have the following interfaces or APIs, which might deserve logging:
- Between the user and the UI (i.e. the UI events, mouse and keyboard)
- Between the UI and the business layer (see "Humble dialog box")
- Between the business layer and the DAL
- Between the DAL and the database
Alternatively the system might be a chain of components connecting two peer end-points (less obviously with "top" and "bottom" layers).
In any case, what I'm logging is the API that's each component's public facade, which is good for logging for several reasons:
- Not too complicated (the facade tends to be simpler than the underlying/internal implementation)
- Good coverage (if I log the whole facade, and if the only way into the component is via its facade, then I know I've logged everything that goes into the component)
- Works well with Conway's Law: when debugging a system with multiple components, each developed by a different team, one of the recurrent questions is, "Which component is at fault, and therefore which team needs to debug it?"