IMHO, for messages that are very likely to be displayed, such as those given to error or warn it does not make much of a difference.
For messages that are less likely displayed, I would definitely go for the second version, mainly for performance reasons. I often give large objects as a parameter to info, which implement a costly __str__ method. Clearly, sending this pre-formatted to info would be a performance waste.
UPDATE
I just checked the source code of the logging module and, indeed, formatting is done after checking the log level. For example:
class Logger(Filterer):
# snip
def debug(self, msg, *args, **kwargs):
# snip
if self.isenabledfor(debug):
self._log(debug, msg, args, **kwargs)
One can observe that msg and args are untouched between calling log and checking the log level.
UPDATE 2
Spired by Levon, let me add some tests for objects that have a costly __str__ method:
$ python -m timeit -n 1000000 -s "import logging" -s "logger = logging.getLogger('foo')" -s "logger.setLevel(logging.ERROR)" "logger.warn('%s', range(0,100))"
1000000 loops, best of 3: 1.52 usec per loop
$ python -m timeit -n 1000000 -s "import logging" -s "logger = logging.getLogger('foo')" -s "logger.setLevel(logging.ERROR)" "logger.warn('%s' % range(0,100))"
1000000 loops, best of 3: 10.4 usec per loop
In practice, this could give a fairly high performance boost.
warnis made. Not disputing what you are saying, just curious to find out more.logging.warn- in the second case, we're just passing a list of args tologging.warn(which is merely tuple creation?) - so we avoid a string formatting operation at the invocation. As I mentioned in the question, I assume that if the current logging level would filter out the log message, no formatting would be done, again avoiding the string formatting operation. My conjecture is this should save processing time and memory allocations.