Python › Logging

What's the danger of using %-style, f-string, or .format() interpolation directly in a log call's message argument versus lazy % substitution?

Intermediate

Answer

Calling logging.info(f'User {user} logged in') always evaluates the f-string eagerly, even if the log level would suppress the message entirely -- wasting CPU on string formatting that's discarded. Using logging.info('User %s logged in', user) instead defers formatting until (and unless) the record is actually emitted, which matters for performance in hot paths with expensive-to-format arguments.
# Eager (wasteful if DEBUG is disabled):
logging.debug(f'Processing {expensive_repr(obj)}')

# Lazy (only formats if DEBUG level is active):
logging.debug('Processing %s', expensive_repr_callable())  # still eager unless wrapped
logging.debug('Processing %s', obj)  # str(obj) deferred until formatting time
Real-world example A high-throughput service avoids f-strings in debug-level logs on hot code paths, using %-style args instead, to prevent formatting overhead when DEBUG logging is disabled in production.

Common follow-ups: Does %-style substitution fully avoid the cost if args themselves are expensive to compute?;How does isEnabledFor() help guard expensive log calls?