Loki Logger Handler
Loki Logger Handler is a Python logging handler designed for transmitting logs to Grafana Loki. It formats logs in JSON by default, allows custom label definitions, and supports extracting extra keys from log records as labels or structured metadata. The library is currently at version 1.1.2 and appears to be under active development with regular updates addressing compatibility and feature enhancements.
Common errors
-
AttributeError: 'LoggerFormatter' object has no attribute 'message_in_json_format'
cause This error occurred in version 1.1.1 when `message_in_json_format` was set to `False` due to a bug in attribute handling.fixUpgrade the `loki-logger-handler` library to version 1.1.2 or newer. -
Logs are not appearing in Grafana Loki, or I see HTTP 400 Bad Request errors in my application logs when sending to Loki.
cause This usually indicates an incorrect Loki URL, network connectivity issues, or Loki rejecting the log entries due to validation errors (e.g., missing stream labels, line_too_long, or invalid JSON format).fixDouble-check the `url` parameter in `LokiLoggerHandler` for correctness, including the `/loki/api/v1/push` endpoint. Verify network access to the Loki instance. Ensure that `labels` are provided and that log lines do not exceed Loki's maximum entry size (default 256KB). -
Duplicate log entries appear when running tests or reloading my application.
cause This is a common Python `logging` module issue where handlers are added multiple times to the same logger instance without being removed.fixEnsure `logger.addHandler()` is called only once for each handler. In testing scenarios, call `logger.removeHandler(handler)` in teardown or use `logging.basicConfig` with `force=True` (Python 3.8+) or check `if not logger.handlers:` before adding handlers. -
KeyError: 'some_extra_field' when trying to access log.extra in a custom formatter or `label_keys` is not working.
cause The `extra` dictionary was not passed to the logging call, or the specified key was not present in the `extra` dictionary provided.fixWhen logging, ensure you pass the `extra` dictionary with the keys you expect: `logger.info('My message', extra={'some_extra_field': 'value'})`. Verify that `label_keys` or `loki_metadata_keys` correctly list the fields you intend to extract.
Warnings
- breaking Older versions (pre-1.0.0, especially pre-0.1.3) had Python 2/3 compatibility issues related to file operations (`open` vs `io.open`) and dictionary merging syntax, which could lead to runtime errors on specific Python versions.
- gotcha The `auth` attribute for basic authentication was added in version 1.1.1. If you are using an older version and require authenticated access to Loki, this feature will be unavailable.
- gotcha Enabling structured metadata (for Loki 3.0+) requires specific parameters (`enable_structured_loki_metadata`, `loki_metadata`, `loki_metadata_keys`) introduced in version 1.1.0. Using these features with older versions or an incompatible Loki server will not work as expected.
- gotcha An `AttributeError` could be raised in version 1.1.1 when `message_in_json_format` was set to `False` due to an incorrect attribute access. This was fixed in 1.1.2.
- gotcha Avoid using high-cardinality values (e.g., `user_id`, `request_id`, dynamic timestamps) directly as Loki labels. This can lead to performance degradation and increased storage costs in Loki itself.
Install
-
pip install loki-logger-handler
Imports
- LokiLoggerHandler
from loki_logger_handler.loki_logger_handler import LokiLoggerHandler
- LoggerFormatter
from loki_logger_handler.loki_logger_handler import LoggerFormatter
- LoguruFormatter
from loki_logger_handler.loki_logger_handler import LoguruFormatter
Quickstart
import logging
import os
from loki_logger_handler.loki_logger_handler import LokiLoggerHandler, LoggerFormatter
LOKI_URL = os.environ.get('LOKI_URL', 'http://localhost:3100/loki/api/v1/push')
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# Configure Loki Handler
loki_handler = LokiLoggerHandler(
url=LOKI_URL,
labels={
'application': 'my-python-app',
'environment': 'dev'
},
# Optionally, specify keys from 'extra' dict to be used as labels
label_keys={'user_id', 'request_id'},
# For Loki 3.0+ and structured metadata
# enable_structured_loki_metadata=True,
# loki_metadata_keys={'metadata_field'}
)
# Optionally, use a specific formatter (LoggerFormatter is default for standard logging)
loki_handler.setFormatter(LoggerFormatter())
logger.addHandler(loki_handler)
# Example logging
logger.info('Application started successfully.')
logger.warning('Potential issue detected.', extra={'user_id': '12345'})
try:
1 / 0
except ZeroDivisionError:
logger.error('A critical error occurred!', exc_info=True, extra={'request_id': 'abc-123'})