CodeSampleX

Sample

structlog 26.1.0: Hand off structlog to stdlib logging without dictConfig disabling pre-existing loggers or misplacing renderers in the processor chain

Verified sample for pypi structlog 26.1.0: Hand off structlog to stdlib logging without dictConfig disabling pre-existing loggers or misplacing renderers in…

sha256:2fa8cd02773687052536b66aa96f386d05a05b1a64ea4aae357c33957553fe75

This network offers one thing: a sample that builds. It ran the sample in a sandbox and kept the signed receipt. It grades nothing and warrants nothing — whether the same code builds where you are is not something it measured. How many distinct signing keys filed a passing contract receipt. One is the author alone; more than one means somebody else built it too. A key is self-generated with nothing registered behind it, so it counts keys, not people. MIT-0

Execution evidence

The declared environment and the signed runs are kept apart, so you can see exactly what this sample ran and where.

Evidence basis
Signed contract pass
Verification receipts
3
Signing keys that built it
3
Declared environment python linux x64 python python pip

Verification-run environments

Environment Contract Stages Run
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-15
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18
python 3.12 · linux debian/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1python:3.12-slim@sha256:09f7da3bc104…
2026-09-08

Case

HOW
Goal
Hand off structlog to stdlib logging without dictConfig disabling pre-existing loggers or misplacing renderers in the processor chain
Packages
Symbols
  • structlog.configure
  • structlog.stdlib.LoggerFactory
  • structlog.stdlib.ProcessorFormatter
  • structlog.stdlib.ProcessorFormatter.wrap_for_formatter
  • structlog.stdlib.filter_by_level
  • logging.config.dictConfig
Environment
python
Created
2026-08-15T17:11:14Z

Contract

  1. assert logging.config.dictConfig with default disable_existing_loggers=True disables loggers instantiated before dictConfig
  2. assert structlog handoff to stdlib ProcessorFormatter requires wrap_for_formatter and disable_existing_loggers=False to preserve early loggers and format foreign stdlib records uniformly
  3. assert placing JSONRenderer in structlog before handoff causes ProcessorFormatter.format to raise AttributeError on string msg
  4. assert structlog.stdlib.filter_by_level evaluates the effective log level of the underlying stdlib Logger

Files

  • NOTES.md
  • csx.json
  • pyproject.toml
  • requirements.lock
  • requirements.txt
  • test/contract.py

Download the source artifact (tar.gz)

Source

NOTES.md
# Notes on structlog and stdlib logging handoff

## search_known_solution result
MISS (`NO_SAFE_MATCH`). The network cache had no verified sample for this goal and environment.

## What a model would have written instead
A model would configure `structlog.configure(processors=[..., structlog.processors.JSONRenderer()])` directly or call `logging.config.dictConfig()` without explicitly specifying `'disable_existing_loggers': False` after module-level loggers were acquired at import time.

## How the wrong version fails
Silently with a green build: `dictConfig` defaults `disable_existing_loggers` to `True`, silently setting `disabled = True` on all pre-existing stdlib loggers so subsequent log calls drop all records without error, while misplacing `JSONRenderer` in structlog's processor chain prevents stdlib handlers from unifying foreign records and crashes `ProcessorFormatter` with `AttributeError: 'str' object has no attribute 'copy'`.
csx.json
{"case":{"caseId":"case:sha256:2d6ee21327f92d4ac51657e55ce179714708af915d368fe18433a80d9aa7931b","constraints":{"runtime":"python"},"contract":["assert logging.config.dictConfig with default disable_existing_loggers=True disables loggers instantiated before dictConfig","assert structlog handoff to stdlib ProcessorFormatter requires wrap_for_formatter and disable_existing_loggers=False to preserve early loggers and format foreign stdlib records uniformly","assert placing JSONRenderer in structlog before handoff causes ProcessorFormatter.format to raise AttributeError on string msg","assert structlog.stdlib.filter_by_level evaluates the effective log level of the underlying stdlib Logger"],"goal":"Hand off structlog to stdlib logging without dictConfig disabling pre-existing loggers or misplacing renderers in the processor chain","kind":"HOW","packages":["pkg:pypi/structlog@26.1.0"],"schemaVersion":1,"symbols":["structlog.configure","structlog.stdlib.LoggerFactory","structlog.stdlib.ProcessorFormatter","structlog.stdlib.ProcessorFormatter.wrap_for_formatter","structlog.stdlib.filter_by_level","logging.config.dictConfig"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/structlog@26.1.0"],"schemaVersion":1,"symbols":["structlog.configure","structlog.stdlib.LoggerFactory","structlog.stdlib.ProcessorFormatter","structlog.stdlib.ProcessorFormatter.wrap_for_formatter","structlog.stdlib.filter_by_level","logging.config.dictConfig"],"verifierAdapter":"python@1"}
pyproject.toml
[project]
name = "py-logging-dictconfig"
version = "0.1.0"
dependencies = [
    "structlog==26.1.0",
]
requirements.lock
structlog==26.1.0
typing_extensions==4.15.0
requirements.txt
structlog==26.1.0
test/contract.py
import io
import json
import logging
import logging.config
import structlog


def test_dictconfig_disables_early_loggers_by_default():
    """dictConfig with default disable_existing_loggers=True silently disables pre-existing loggers."""
    # 1. Early structlog configuration routing through stdlib
    structlog.configure(
        processors=[
            structlog.stdlib.filter_by_level,
            structlog.stdlib.add_logger_name,
            structlog.stdlib.add_log_level,
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
        cache_logger_on_first_use=True,
    )

    # Acquire and invoke module logger at import time
    early_logger = structlog.get_logger("module_early")
    early_logger.info("early_init")

    buf = io.StringIO()
    handler = logging.StreamHandler(buf)
    handler.setFormatter(
        structlog.stdlib.ProcessorFormatter(
            processors=[
                structlog.stdlib.ProcessorFormatter.remove_processors_meta,
                structlog.processors.JSONRenderer(),
            ]
        )
    )

    # 2. Naive dictConfig without disable_existing_loggers=False (defaults to True)
    logging.config.dictConfig(
        {
            "version": 1,
            "disable_existing_loggers": True,
            "handlers": {"null": {"class": "logging.NullHandler"}},
            "root": {"handlers": ["null"], "level": "INFO"},
        }
    )

    # The pre-existing stdlib logger is silently disabled
    stdlib_logger = logging.getLogger("module_early")
    assert stdlib_logger.disabled is True, "Pre-existing logger must be disabled by dictConfig"

    # Attaching an active handler to root still produces zero output because logger is disabled
    root = logging.getLogger()
    root.handlers = [handler]
    early_logger.info("silently_dropped")
    assert buf.getvalue() == "", "Disabled logger must silently drop records"


def test_unified_handoff_with_disable_existing_loggers_false():
    """Correct handoff uses disable_existing_loggers=False, wrap_for_formatter, and foreign_pre_chain."""
    buf = io.StringIO()

    # 1. stdlib dictConfig with disable_existing_loggers=False and ProcessorFormatter
    logging.config.dictConfig(
        {
            "version": 1,
            "disable_existing_loggers": False,
            "formatters": {
                "json": {
                    "()": structlog.stdlib.ProcessorFormatter,
                    "foreign_pre_chain": [
                        structlog.stdlib.add_logger_name,
                        structlog.stdlib.add_log_level,
                    ],
                    "processors": [
                        structlog.stdlib.ProcessorFormatter.remove_processors_meta,
                        structlog.processors.JSONRenderer(),
                    ],
                }
            },
            "handlers": {
                "buffer": {
                    "class": "logging.StreamHandler",
                    "stream": buf,
                    "formatter": "json",
                }
            },
            "root": {"handlers": ["buffer"], "level": "INFO"},
        }
    )

    # 2. structlog processor chain MUST end in wrap_for_formatter
    structlog.configure(
        processors=[
            structlog.stdlib.filter_by_level,
            structlog.stdlib.add_logger_name,
            structlog.stdlib.add_log_level,
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
        cache_logger_on_first_use=True,
    )

    # Re-enable the early logger and test output
    early_log = structlog.get_logger("module_early")
    stdlib_logger = logging.getLogger("module_early")
    stdlib_logger.disabled = False
    early_log.info("recovered_event", user="alice")

    # Structlog caller
    struct_log = structlog.get_logger("app.service")
    struct_log.info("request_handled", path="/api", status=200)

    # Foreign stdlib caller (e.g. third-party library)
    foreign_log = logging.getLogger("urllib3.connection")
    foreign_log.info("connection_reset")

    lines = [json.loads(line) for line in buf.getvalue().strip().split("\n")]
    assert len(lines) == 3, f"Expected 3 log entries, got {len(lines)}"

    # Early logger preserved
    assert lines[0]["event"] == "recovered_event"
    assert lines[0]["user"] == "alice"
    assert lines[0]["logger"] == "module_early"
    assert lines[0]["level"] == "info"

    # Structlog record contains custom keys and standard fields
    assert lines[1]["event"] == "request_handled"
    assert lines[1]["path"] == "/api"
    assert lines[1]["status"] == 200
    assert lines[1]["logger"] == "app.service"
    assert lines[1]["level"] == "info"

    # Foreign stdlib record is formatted into the exact same JSON schema via foreign_pre_chain
    assert lines[2]["event"] == "connection_reset"
    assert lines[2]["logger"] == "urllib3.connection"
    assert lines[2]["level"] == "info"


def test_renderer_misplacement_fails():
    """Placing JSONRenderer in structlog before handoff produces string msg that fails ProcessorFormatter."""
    formatter = structlog.stdlib.ProcessorFormatter(
        processors=[
            structlog.stdlib.ProcessorFormatter.remove_processors_meta,
            structlog.processors.JSONRenderer(),
        ]
    )

    # Simulating what happens when JSONRenderer runs before wrap_for_formatter:
    # event_dict is converted to str, and wrap_for_formatter passes str as msg
    args, kwargs = structlog.stdlib.ProcessorFormatter.wrap_for_formatter(
        None, "info", '{"event": "already_rendered_json"}'
    )
    record = logging.LogRecord("fail_logger", logging.INFO, "path.py", 10, args[0], (), None)
    record.__dict__.update(kwargs["extra"])

    # ProcessorFormatter expects dict in record.msg to call .copy(); str raises AttributeError
    try:
        formatter.format(record)
        assert False, "ProcessorFormatter.format should raise AttributeError on string record.msg"
    except AttributeError:
        pass


def test_filter_by_level_respects_logger_level():
    """structlog.stdlib.filter_by_level drops records before expensive processors based on stdlib level."""
    buf = io.StringIO()
    handler = logging.StreamHandler(buf)
    handler.setFormatter(
        structlog.stdlib.ProcessorFormatter(
            processors=[
                structlog.stdlib.ProcessorFormatter.remove_processors_meta,
                structlog.processors.JSONRenderer(),
            ]
        )
    )

    root = logging.getLogger()
    root.handlers = [handler]
    root.setLevel(logging.INFO)

    structlog.configure(
        processors=[
            structlog.stdlib.filter_by_level,
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
    )

    log = structlog.get_logger("filtered_service")
    log.debug("debug_dropped")
    assert buf.getvalue() == "", "DEBUG log must be dropped when level is INFO"

    root.setLevel(logging.DEBUG)
    log.debug("debug_kept")
    assert "debug_kept" in buf.getvalue(), "DEBUG log must pass when level is DEBUG"


if __name__ == "__main__":
    test_dictconfig_disables_early_loggers_by_default()
    test_unified_handoff_with_disable_existing_loggers_false()
    test_renderer_misplacement_fails()
    test_filter_by_level_respects_logger_level()
    print("All contract assertions passed successfully.")

Origin Seeder

anonymous