I wanted to see what Pierre-Yves had to say before jumping in...
My experience has been that while logging is used somewhat less in FP, it
is still fairly widely used. Two common idioms are returning all the logging data along with the actual result (as Pierre-Yves suggested), or some sort of fire-and-forget asynchronous logging which can be easily ignored or mocked for development/testing. An important mental shift in both cases is to think of logging output as data, not text. For a good talk about logs-as-data watch
this talk from Clojure/conj 2012.
In our production Clojure applications, we still have some "regular" (text) logging, output asynchronously (fire-and-forget), but increasingly we're writing "logging" data to files, for aggregation and processing elsewhere. We treat that the same way as we treat other side-effects by pushing them to the edges of our code as much as possible. We often have the idea of a "commit" at the end of any given piece of processing and our computation hands back the result to be returned and a list of thunks to evaluate -- anonymous functions that take no arguments. That list of thunks will include store-to-database side effects or log-to-file side effects or whatever else needs to be done.
We've also experimented with returning a pure data structure that represents all the things that need committing. That's "nicer" from a functional perspective but not as convenient. You can take a look at a
library of mine that supports this approach. We used that for a while at work but it was rather unwieldy in production usage.
Whatever approach you take, the key things are to try to keep most of your code pure, and push side-effects out to the edges, as much as is practical.