As with so many categorical guidelines, there are circumstances to use it and circumstances to not. The key insight is that printf() is a heavyweight operation ("What, you want to build a string? A human readable string? Okay, one second, lemme just pull in the locale library..."). If you're debugging something at the business-logic layer, it's probably fine. If you're debugging a memory leak, calling a function that…
Isn't localization triggered for specific format strings only? There are many corner cases when typical printf implementations call malloc, but it's usually not too hard to avoid them. If you are worried about including and potential side effects from that, you can use __builtin_printf instead.
When I say move memory around in this context, I mean do a lot of stack operations. You can leak from the stack too (drop a pointer from the stack without freeing the underlying heap memory it referenced), and it's harder to catch that if printf has come along and completely rewritten your unused stack memory as consequence of reporting on the state of your program.
That having been said, the point is that context matters and what you're debugging matters for the question of what tool to use. If you're operating in an interpreted language, you can probably trust that The interpreter is making it difficult to leak memory like that. On the other hand, interpreters have bugs too, and using a language that is interpreted instead of compiled machine code makes bugs in the execution layer unlikely, but not impossible...