Lazy Logger Formatting

ID

python.logging_no_format

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

Python

Tags

best-practice, efficiency

Description

Reports logger calls whose first argument is a pre-formatted string — logger.info("got %s" % name), logger.info(f"got {name}"), logger.info("got " + str(name)), logger.info("got {}".format(name)). The logging module supports lazy interpolation: pass the format string and the arguments separately, and the interpolation runs only if the level is actually enabled.

logger.info("got %s" % name)            # FLAW
logger.info(f"got {name}")              # FLAW
logger.info("got " + str(name))         # FLAW
logger.info("got {}".format(name))      # FLAW

logger.info("got %s", name)             # OK

Rationale

In a production service the bulk of the log calls are at DEBUG level and are not enabled. The eager forms still build the formatted string for every one of those calls, dominating the work the logging package is doing. The lazy form delegates the format step to the logger, which only runs it when the message is actually emitted.

The lazy form also integrates with structured-log handlers that want the args as fields rather than embedded in the message text.

Remediation

Pass the format string and args separately:

logger.info("got %s items in %.2fs", count, elapsed)

# For exception logging:
logger.exception("processing %s failed", item_id)