mrkeyoor.com_
Sun 20 Sept 11:42 UTC
PyPIInfraupdated 20 Sept 2026

python-json-logger review

python-json-logger 4.2.0 supplies JSON formatters for Python's built-in `logging` package. Handlers, logger hierarchy, filters, and propagation stay standard; the formatter turns `LogRecord` fields and `extra` data into one JSON object per event. It can rename fields, add static values, render tracebacks as arrays, and use optional orjson or msgspec serializers. Version 4.2.0 stops mutating a dictionary passed as the log message when exception or stack fields are added, and it recognizes unbraced `$name` placeholders in dollar-style formats.

Verdict

python-json-logger 4.2.0 installed in 0.3 seconds as 1 dependency-free package, occupied 1 MB, imported in 0.17 seconds, and had 0 audit findings in our sandbox. Install it to convert an existing Python logging graph to collector-friendly JSON; skip it when you need a full event-processing model or a ready-made vendor schema.

We installed it

Lab card: what happened when we installed python-json-loggerScreenshot of python-json-logger documentation
Install✓ · 0.3s1 package on disk · 1 MB
Importimport pythonjsonlogger in 0.17s · pure Python · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does python-json-logger install cleanly?

Yes. In a fresh container with an empty cache, pip install python-json-logger finished in 0.3s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does python-json-logger need to run?

Python >=3.10, and nothing compiled: it is pure Python. In our run import pythonjsonlogger succeeded in 0.17s, and the package ships py.typed for type checkers.

python-json-logger or structlog: which should you use?

structlog: Use it when bound context, processor pipelines, and multiple renderers should define the logging model. python-json-logger 4.2.0 installed in 0.3 seconds as 1 dependency-free package, occupied 1 MB, imported in 0.17 seconds, and had 0 audit findings in our sandbox.

When should you not use python-json-logger?

You need context binding and processor chains before an event becomes a LogRecord. structlog owns that workflow more directly.

API stability4/5The central contract remains a `logging.Formatter` attached to ordinary handlers, and version 4.2.0 only corrects dictionary mutation and dollar-style field parsing. Version 4 did rename hook arguments and remove string substitutes for serializer objects, so custom subclasses and old `dictConfig` files need a migration review. Ordinary `JsonFormatter` use stays close to Python logging conventions.
Docs5/5The versioned site has a quickstart, formatter reference, cookbook, migration notes, changelog, and dedicated pages for standard JSON, orjson, and msgspec backends. Examples cover `dictConfig`, field selection, renaming, static values, exception arrays, custom defaults, and subclass hooks. The docs also distinguish compatibility imports from the preferred version 4 module paths.
Maintenance5/5Version 4.2.0 and the latest repository push both landed on 15 August 2026. GitHub reports 267 stars, 8 combined open issues and pull requests, and an unarchived repository. The release fixes caller-dictionary mutation and dollar-style parsing, following version 4.1 support for Python 3.14 and the removal of Python 3.8 and 3.9.
Ecosystem5/5PyPI Stats recorded 26,652,410 downloads in the latest week. Because it attaches to standard handlers, the formatter captures records from frameworks and dependencies already using Python logging. Optional orjson and msgspec backends cover alternate serializers, and filters can inject tracing context. Vendor-specific schemas, transport, retention, and secret filtering still belong elsewhere.

Use it if

  • An existing standard-logging setup must emit one JSON object per line for a collector or container runtime.
  • Third-party libraries already log through `logging`, and their records should enter the same structured output without API rewrites.
  • Field renaming, static service metadata, or array-form tracebacks can bridge records to an ingestion schema.
  • The team wants a formatter layer while retaining standard handlers, filters, levels, and `dictConfig`.
Skip it if

Setup reality

We installed python-json-logger 4.2.0 in a fresh Python 3.12 Bookworm sandbox in 0.3 seconds. It left 1 package using 1 MB, and import pythonjsonlogger completed in 0.17 seconds. pip-audit found 0 known vulnerabilities. The distribution is pure Python, has 0 direct dependencies, includes py.typed, and requires Python 3.10 or newer. PyPI does not supply a license value, while the repository identifies BSD-2-Clause.

No account or network setup is required. Attach pythonjsonlogger.json.JsonFormatter to the handler that writes toward the collector, preferably stdout in a container. The format selects and orders standard fields; values passed through extra are included unless reserved. An extra key that collides with a LogRecord attribute raises KeyError before the formatter runs, so settle names such as request_id, tenant_id, and trace_id centrally.

JSON output does not solve schema design or redaction. rename_fields can map levelname to severity, and static_fields can stamp service metadata, but request values should come from extra or a filter. A value unknown to the JSON encoder needs json_default. Optional OrjsonFormatter and MsgspecFormatter require their respective packages, which were not present in our 1-package install. Importing those modules without the extra serializer raises a package error.

Tracebacks are strings by default and contain embedded newlines. exc_info_as_array and stack_info_as_array make them easier for line-oriented collectors. Version 4.2.0 matters when callers log a dictionary: adding exception data no longer inserts fields into that same object. Formatter hooks changed argument names in version 4, and string paths for serializer callables were removed; dictConfig callers should use ext:// resolution or pass callable objects directly.

Patterns

Write JSON records to stdout attach-json-handler

import logging
import sys
from pythonjsonlogger.json import JsonFormatter

handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter(
    '%(asctime)s %(levelname)s %(name)s %(message)s'
))
log = logging.getLogger('billing')
log.setLevel(logging.INFO)
log.addHandler(handler)
log.info('worker ready')

Use `pythonjsonlogger.json` for version 4 code; the older `jsonlogger` import path remains mainly for compatibility.

Attach fields to one event add-event-fields

log.info(
    'payment captured',
    extra={'order_id': 991, 'amount_cents': 4200},
)

An `extra` key matching 1 built-in `LogRecord` attribute raises `KeyError` before JSON formatting.

Send a dictionary as the message log-dict-event

event = {
    'message': 'payment captured',
    'order_id': 991,
    'amount_cents': 4200,
}
log.info(event)

Version 4.2.0 no longer adds `exc_info` or `stack_info` to the caller's dictionary while formatting it.

Match collector field names rename-output-fields

formatter = JsonFormatter(
    '%(asctime)s %(levelname)s %(message)s',
    rename_fields={
        'asctime': 'timestamp',
        'levelname': 'severity',
    },
)

Only selected source fields can be renamed; include both original names in the format before mapping them.

Add fixed service metadata stamp-service-fields

formatter = JsonFormatter(
    static_fields={
        'service': 'checkout-api',
        'environment': 'production',
    },
)

Static fields repeat on every record; request-specific values belong in `extra` or a context-aware filter.

Create the formatter with dictConfig configure-dictconfig

import logging.config

logging.config.dictConfig({
    'version': 1,
    'formatters': {
        'json': {
            '()': 'pythonjsonlogger.json.JsonFormatter',
            'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
        },
    },
    'handlers': {
        'stdout': {
            'class': 'logging.StreamHandler',
            'formatter': 'json',
            'stream': 'ext://sys.stdout',
        },
    },
    'root': {'level': 'INFO', 'handlers': ['stdout']},
})

The `()` entry tells `dictConfig` to construct the version 4 JSON formatter with the remaining values.

Encode traceback lines as arrays array-traceback-lines

formatter = JsonFormatter(
    '%(levelname)s %(message)s %(exc_info)s %(stack_info)s',
    exc_info_as_array=True,
    stack_info_as_array=True,
)

Array output keeps each traceback line separate instead of embedding multiple newline characters in 1 JSON string.

Encode Decimal values serialize-custom-type

from decimal import Decimal
from pythonjsonlogger import defaults

def encode(value):
    if isinstance(value, Decimal):
        return str(value)
    return defaults.json_default(value)

formatter = JsonFormatter(json_default=encode)

Delegate unknown objects to the package default so 1 new value type does not break the whole log record.

Select the optional orjson backend use-orjson-formatter

from pythonjsonlogger.orjson import OrjsonFormatter

handler.setFormatter(OrjsonFormatter(
    '%(levelname)s %(message)s'
))

Install `orjson` separately; it was absent from our 1-package base install and the optional module fails without it.

Read a request id from ContextVar inject-request-context

import logging
from contextvars import ContextVar

request_id = ContextVar('request_id', default=None)

class RequestFields(logging.Filter):
    def filter(self, record):
        record.request_id = request_id.get()
        return True

handler.addFilter(RequestFields())

A handler filter enriches records from third-party modules too, as long as they reach the same handler.

Remove known secrets before serialization redact-secret-fields

class RedactingFormatter(JsonFormatter):
    def process_log_record(self, data):
        for field in ('password', 'access_token', 'authorization'):
            if field in data:
                data[field] = '[redacted]'
        return data

The base formatter applies no application-specific redaction; keep the sensitive-field list tied to your actual event schema.

Nest remaining attributes reshape-log-document

class EventFormatter(JsonFormatter):
    def process_log_record(self, data):
        return {
            'timestamp': data.pop('asctime', None),
            'severity': data.pop('levelname', None),
            'message': data.pop('message', None),
            'attributes': data,
        }

`process_log_record` is the final version 4 transformation hook; custom subclasses from 3.x need their argument names reviewed.

Alternatives

PackageRegistryPick it when
structlogPyPIUse it when bound context, processor pipelines, and multiple renderers should define the logging model.
ecs-loggingPyPIUse it when logs must follow Elastic Common Schema without hand-written field mapping.
loguruPyPIUse it when replacing standard logging with managed sinks, context, rotation, and formatting is acceptable.
orjsonPyPIUse it directly when the task is fast JSON serialization rather than integration with Python logging records.

More infra guides

boto3 · opentelemetry-api · psutil · distro · @opentelemetry/api · google-cloud-storage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.