logfmter
logfmter is a Python package that enables logfmt-formatted logging using the standard `logging` module without requiring changes to existing log calls. It aims to provide human and machine-readable logs, adhering to best practices like those recommended by Splunk. The current version is 0.0.12, and new releases are typically made for bug fixes, Python version support, and minor feature enhancements.
Common errors
-
Logs are not appearing in logfmt format / Not seeing any logfmter output.
cause `Logfmter` is not correctly applied to a `logging.Handler`, or the handler is not attached to the logger. When using `dictConfig`, `disable_existing_loggers` might be `True` by default, disabling other loggers.fixEnsure a `Logfmter` instance is set as the formatter for at least one `StreamHandler` or `FileHandler`, and that handler is added to the relevant logger (or root logger) using `basicConfig` or `dictConfig`. If using `dictConfig`, explicitly set `"disable_existing_loggers": false` if you need existing loggers to continue logging. -
Logs show \n characters literally instead of creating newlines.
cause Starting from `logfmter` v0.0.4, newline characters are explicitly escaped in all logged values to ensure single-line output. This is the intended behavior.fixThis is the designed behavior since v0.0.4. If multi-line log statements are desired (which goes against the logfmt philosophy), either downgrade `logfmter` to a version prior to 0.0.4 (not recommended, as it fixes a bug) or adjust logging to avoid embedding newlines. -
ModuleNotFoundError: No module named 'logfmter'
cause The `logfmter` package is not installed in the current Python environment.fixInstall the package using pip: `pip install logfmter`. -
AttributeError: 'Logfmter' object has no attribute 'formatTime'
cause Attempting to customize date format using `formatter.datefmt` directly on the `Logfmter` instance instead of passing `datefmt` during initialization.fixPass the `datefmt` parameter directly to the `Logfmter` constructor, e.g., `Logfmter(datefmt="%Y-%m-%d %H:%M:%S")`.
Warnings
- breaking Python 3.9 support was dropped in `logfmter` v0.0.12. Users on Python 3.9 or older must pin their dependency to `logfmter<0.0.12`.
- breaking Starting from v0.0.4, newline characters (`\n`) in all logged values are explicitly escaped to ensure single-line output per log record. If your application previously relied on generating multi-line log statements by embedding newlines, this behavior will change, and `\n` will appear literally in the output.
- breaking In v0.0.10, proper escaping of backslashes was introduced to fix issues with log consumption by third-party tools. This is a fix, but it might alter the literal string representation of backslashes in your log output if previous versions had different (and potentially incorrect) behavior.
- gotcha When using `logging.config.dictConfig` to configure logging, the `disable_existing_loggers` parameter is `True` by default. This can inadvertently disable loggers from third-party modules that were configured before `dictConfig` is called, leading to missing log output from those libraries.
- gotcha `logfmter` does not officially support configuration via `logging.config.fileConfig`. This is due to `fileConfig`'s limitation in supporting custom formatter initialization, which `logfmter` relies on.
Install
-
pip install logfmter
Imports
- Logfmter
from logfmter import Logfmter
Quickstart
import logging
import os
from logfmter import Logfmter
# Configure a basic StreamHandler with Logfmter
handler = logging.StreamHandler()
handler.setFormatter(Logfmter())
# Basic configuration to use the Logfmter handler
logging.basicConfig(handlers=[handler], level=os.environ.get('LOG_LEVEL', 'INFO'))
# Example logs
logging.info("Application started", env=os.environ.get('ENV', 'development'))
logging.warning("User login failed", user_id=123, ip_address="192.168.1.1", extra={"attempts": 3})
logging.error("An unexpected error occurred", error_code="E1001")
# Logging a dictionary directly as a message (keys will be flattened)
logging.info({"event": "user_data_processed", "status": "success", "records": 100})