A tool that automatically converts f-strings in Python logging calls into lazy logging calls for consistency with Python documentation, improved performance and linting compliance.
See PyPI page for more details. See Changelog for release notes.
Example:
# Before (f-string in log call; not recommended)
logger.info(f"On {datetime.now()} temperature in {city} is {temp:.2f}°C.")
# After lazy-log-formatter
logger.info("On %s temperature in %s is %.2f°C.", datetime.now(), city, temp)
# Prints: On 2024-06-01 12:00:00 temperature in Madrid is 21.50°C.In Python, the recommended way to log variable data is to use format-string placeholders and pass values separately:
import logging
logging.warning('%s before you %s', 'Look', 'leap!')This approach:
- ensures string formatting and interpolation are only performed if the message is actually emitted (e.g. based on the logging level),
- is consistent with Python’s logging design and documentation,
- prevents linting alerts (e.g. Pylint’s W1203:
Use %s formatting in logging functions).
- Scans Python files for f-strings used in logging calls.
- Provides an option to automatically convert f-strings in logging calls to lazy logging calls.
- Can be integrated as a pre-commit hook to enforce logging best practices in codebases.
Install from PyPI:
pip install lazy-log-formatterYou can either run the tool as a Python module, use it as a pre-commit hook, run the entry point script:
lazy-log-formatter [OPTIONS] [PATH...]or
python -m lazy_log.cli [OPTIONS] [PATH...]Add the following to your .pre-commit-config.yaml:
- repo: https://github.com/dmar1n/lazy-log-formatter
rev: 0.11.0
hooks:
- id: lazy-log-formatter
args: ['--fix']You can run the tool from the command line using the following options:
| Option | Description |
|---|---|
--fix |
Converts f-strings in log calls to lazy logging syntax |
--exclude [PATTERN...] |
Excludes files/directories matching one or more patterns |
PATH [PATH...] |
One or more paths to scan (defaults to current directory) |
Check all Python files in the current directory and subdirectories:
lazy-log-formatter .Fix all Python files in the current directory and subdirectories:
lazy-log-formatter . --fixCheck all Python files in two directories:
lazy-log-formatter lazy_log/ tests/Check specific files:
lazy-log-formatter lazy_log/cli.py tests/data/test.pyExclude specific files or directories:
lazy-log-formatter tests/data --exclude "*.pyc" "__pycache__/*" Fix issues in all Python files in a directory:
lazy-log-formatter mydir --fix# Before
logger.info(f'Hello {name}')
# After
logger.info('Hello %s', name)# Before
logger.info(f'Hello {name} {surname}')
# After
logger.info('Hello %s %s', name, surname)import logging
from datetime import datetime
logger = logging.getLogger(__name__)
def log_and_return_datetime():
now = datetime.now()
logger.info(f"Current datetime: {now}")
return now
class DateTimeLogger:
def log_datetime(self):
now = datetime.now()
logger.info(f"Current datetime: {now}")
return nowAfter running the formatter without --fix, the output will be:
F-strings found in '...\tests\data\test.py':
- F-string in logging call at ...\tests\data\test.py:8: f'Current datetime: {now}'
- F-string in logging call at ...\tests\data\test.py:18: f'Current datetime: {now}'
After running the formatter with --fix, the output will be:
F-strings found and fixed in '...\tests\data\test.py'.
The transformed code will be:
import logging
from datetime import datetime
logger = logging.getLogger(__name__)
def log_and_return_datetime():
now = datetime.now()
logger.info("Current datetime: %s", now)
return now
class DateTimeLogger:
def log_datetime(self):
now = datetime.now()
logger.info("Current datetime: %s", now)
return nowWhen transforming code, the tool uses Black to reformat the modified files. If your project already uses Black, the changes produced by this tool will be consistent with Black’s formatting style.
The tool includes logic to detect logging calls based on the assumption that your logger instances follow common naming conventions (e.g., logger.info(...), log.info(...)).
If a logger variable does not contain the substring "log" in its name, the tool will ignore it.
Only works with the native Python logging module. Other libraries, such as loguru, do not support lazy calls.
For loguru, see Lazy evaluation of expensive functions:
logger.opt(lazy=True).debug("If sink level <= DEBUG: {x}", x=lambda: expensive_function(2**64))