Python provides more than one way to format strings: %-formatting, str.format(), string.Template and f-strings. What format developers use is the matter of personal aesthetic reasons rather than anything else. Still there are use cases where good old printf-style %-formatting may have advantages over other formats. Python’s logging module is one of these cases. Let’s see what are the main reasons to use %-formatting for logging in Python.

Python logging optimization: format string until it cannot be avoided

Python logging methods like debug get in message argument along with optional args and kwargs . Args used for message formatting. Formatting of these arguments is deferred until it cannot be avoided. That means the final message is not evaluated if its log level is below the logger’s log level. On the other hand, f-string is really an expression that evaluated at runtime and it lacks logging’s optimizations. Let’s see an example:

In [ 1 ]: import logging ... : logging . basicConfig ( level = logging . INFO ) ... : logger = logging . getLogger ( 'TestLogger' ) In [ 2 ]: class A : ... : def __str__ ( self ): ... : print ( 'Unnecessary slow computations are done here!' ) ... : return self . __class__ . __name__ ... : ... : obj = A () In [ 3 ]: logger . debug ( "log level below INFO with args: %s " , obj ) In [ 4 ]: logger . debug ( f "log level below INFO with f-string: {obj}" ) Unnecessary slow computations are done here ! In [ 5 ]: logger . debug ( "log level below INFO with args: %s " % obj ) Unnecessary slow computations are done here ! In [ 6 ]: logger . info ( "log level INFO with args: %s " , obj ) Unnecessary slow computations are done here ! INFO : TestLogger : log level INFO with args : A

As you can see in step 3 a message doesn’t get logged as the root logger’s level is higher than debug . Logging module’s inner optimization makes sure that the message is not formatted in this case. Compare this to step 6.

In step 4 the f-string is also not logged because of the low log level. But it’s formatted anyway. That’s why you see obj.__str__ called.

Please note, that logging optimization doesn’t work for "format string" % values format. You can see that in step 5. The magic applies for %-format message if and only if the values for message formatting are passed in as logger method’s arguments!

So step 3 shows the optimized way to do message formatting in logging.

By the way, logging module has other nice optimizations that worth learning.