Over the years I have come to the following rule: during the development design-code-test loop, if the code does not work the first time it gets a logging line where it failed. It helps with debugging right now, and makes a sharp edge surface by some definition. You don't know what you don't know, so you might as well have information gathering around entropy-generating places.
Other than that:
* Don't log in high throughput loops (test them extra!); if you must, use a rate limiting logger with expiring delay.
* Log important parameters
* Log assumptions. Especially about data. There's always way more entropy in data.
* This applies to user-reported bugs, which is a form of a failed assumption. All information necessary to debug the issue now must go to the log.
Also end-to-end testing >> unit testing