I've seen advice to pass a format string and arguments separately when using Python's logging module, for example logger.info("User %s logged in", username), instead of constructing the message with an f-string. Why is this considered better, and does the performance difference actually matter? I'm also curious whether structured logging changes the recommendation.
3 Answers
For new applications, structured logging is often the better long-term approach. Store a stable event name or message plus fields such as user_id, system, and error_code, then let the logging handler render them as text or JSON. That makes searching, grouping, dashboards, and alerting much more reliable than parsing many unique f-string messages.
The performance difference usually isn’t important unless logging happens frequently at disabled levels or inside a tight loop. For a message that is definitely going to be emitted, an f-string may be perfectly readable, but using the lazy form consistently is safer for libraries and shared code. Linters can also enforce this convention so it doesn’t have to be debated in every review.
There are reasons beyond speed. The original template and its arguments remain separate in the log record, which helps aggregation tools group identical events instead of treating every rendered value as a different message. Also, formatting happens inside the logging machinery, so an unusual __str__ or __repr__ failure is reported as a logging error rather than immediately interrupting the code that attempted to log something.

Even when structured logging isn’t available, passing arguments separately gives you some of the same benefits and is the standard approach for the built-in logging API.