Very nice. I like the solution you give - just pass a function.
I think the one thing I wanted to see was that much of the time, the correct answer is “don’t log”. Succeed, or return errors. Period. Let the caller worry about logging. It should be very rare that a reusable library needs to log.