Multi-level logging systems. I find use for at least 5 levels:
verbose: stuff you only want to see when debugging low-level stuff like protocol errors; may be compiled out of release binaries, so you can put stuff in this level that you don't want your end users to find
internal: lower-level tracing than normal level, which is frequently useful, but not so often that you want to see it all the time
normal: default output level, for stuff useful to anyone watching the system run normally
problem: run-time errors that the program knows how to cope with; you might choose to run at this level for release versions, instead of normal
fatal error: "scream and die" type messages, like out-of-memory
Multi-level logging lets you build plenty of logging info into the program without having to see it all the time. You turn up the log level only when you have to debug something, then put it back to its normal level. When doing "printf" type debugging -- temporary messages -- I put them in at normal level, so I don't have to crank up the log level to see them, and have them be obscured by the noisy internal or verbose level messages.