This rule raises an issue when logging statements violate established Python logging best practices, including using eager string formatting, deprecated methods, or attribute name collisions in the 'extra' parameter.

Why is this an issue?

Python’s logging module has specific patterns that ensure correctness, performance, and maintainability. Violating these patterns can lead to several problems:

Eager String Formatting

When you format strings before passing them to logging methods (using f-strings, .format(), % operator, or + concatenation), Python performs the string formatting operation immediately, even if the log message won’t be output due to the current log level. This wastes CPU cycles on string operations that may never be used.

The logging module supports lazy formatting by accepting format arguments as separate parameters. The formatting only occurs when the logging framework confirms the message will actually be logged based on the configured log level.

Deprecated Methods

The logging.warn() and logging.Logger.warn() methods are deprecated. While they still function, they may be removed in future Python versions. The recommended methods are logging.warning() and logging.Logger.warning(), which are functionally equivalent.

Attribute Name Collisions

The logging module’s extra keyword argument allows passing additional values to be included in log records. However, if the keys in the extra dictionary clash with built-in LogRecord attributes (such as 'name', 'msg', 'args', 'created', 'filename', 'funcName', 'levelname', 'levelno', 'lineno', 'module', 'msecs', 'message', 'pathname', 'process', 'processName', 'relativeCreated', 'thread', 'threadName'), a KeyError will be raised when the LogRecord is constructed, causing the logging statement to fail at runtime.

What is the potential impact?

Performance degradation: Eager string formatting can significantly impact application performance, especially in code paths with frequent logging statements. When log levels are configured to filter out certain messages (for example, DEBUG messages in production), the application still pays the cost of formatting strings that will never be logged.

Runtime errors: Attribute name collisions in the extra parameter raise KeyError exceptions, causing logging statements to fail unexpectedly.

Maintenance issues: Using deprecated methods creates technical debt. When these methods are eventually removed from Python, code using them will break. Additionally, mixing deprecated and non-deprecated methods across a codebase creates inconsistency and confusion.

Inconsistent logging: Eager formatting can lead to inconsistent behavior across different log handlers. When using lazy formatting with the extra parameter, all handlers receive the same structured data and can format it consistently according to their configuration.

How to fix it

Use lazy formatting by passing format arguments as separate parameters to logging methods instead of formatting strings before the call. Replace f-strings, .format(), % operator, and + concatenation with %-style placeholders and separate arguments.

Code examples

Noncompliant code example

import logging

user = "Maria"
logging.info(f"{user} - Something happened")  # Noncompliant: f-string formatting

logging.info("{} - Something happened".format(user))  # Noncompliant: .format() method

logging.info("%s - Something happened" % user)  # Noncompliant: % operator formatting

logging.info(user + " - Something happened")  # Noncompliant: string concatenation

Compliant solution

import logging

user = "Maria"
logging.info("%s - Something happened", user)  # Compliant: lazy formatting with separate argument

Resources

Documentation