{"id":7621,"library":"pytest-structlog","title":"pytest-structlog","description":"pytest-structlog is a pytest plugin that provides structured logging assertions for structlog. It integrates a 'log' fixture into your tests, allowing you to capture and make assertions against structlog events. The library is currently at version 1.2 and maintains a fairly active release cadence, with new versions typically released monthly or quarterly.","status":"active","version":"1.2","language":"en","source_language":"en","source_url":"https://github.com/wimglenn/pytest-structlog","tags":["pytest","structlog","testing","logging","assertions","plugin"],"install":[{"cmd":"pip install pytest-structlog","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Required as a pytest plugin for test execution and fixture management.","package":"pytest","optional":false},{"reason":"The core library for structured logging that this plugin asserts against.","package":"structlog","optional":false}],"imports":[{"note":"Used for type hinting the 'log' fixture in tests. The fixture itself is automatically available.","symbol":"StructuredLogCapture","correct":"from pytest_structlog import StructuredLogCapture"},{"note":"This is how you obtain a logger in `structlog` for the code under test.","symbol":"get_logger","correct":"import structlog; logger = structlog.get_logger()"}],"quickstart":{"code":"import structlog\nfrom pytest_structlog import StructuredLogCapture\n\n# your_lib.py (or code under test)\nlogger = structlog.get_logger()\n\ndef do_something():\n    logger.info(\"Starting process\", task_id=\"abc-123\")\n    for i in range(2):\n        logger.debug(\"Step complete\", step=i)\n    logger.warning(\"Process finished with warnings\", result=\"partial\", count=2)\n\n# test_your_lib.py\ndef test_do_something(log: StructuredLogCapture):\n    assert len(log.events) == 0 # No logs captured initially\n\n    do_something()\n\n    # Assert on specific event counts\n    assert log.count(\"Starting process\", level=\"info\") == 1\n    assert log.count(\"Step complete\", level=\"debug\") == 2\n\n    # Assert on individual events with context\n    assert log.has(\"Starting process\", task_id=\"abc-123\")\n    assert log.has(\"Step complete\", step=0)\n    assert log.has(\"Step complete\", step=1)\n    assert log.has(\"Process finished with warnings\", result=\"partial\", count=2, level=\"warning\")\n\n    # You can also inspect all captured events directly\n    expected_events = [\n        log.info(\"Starting process\", task_id=\"abc-123\"),\n        log.debug(\"Step complete\", step=0),\n        log.debug(\"Step complete\", step=1),\n        log.warning(\"Process finished with warnings\", result=\"partial\", count=2)\n    ]\n    assert log.events == expected_events","lang":"python","description":"This quickstart demonstrates how to use the `log` fixture provided by pytest-structlog. It shows how to capture structlog events from a function under test and then assert their presence and content using `log.events`, `log.has()`, and `log.count()`."},"warnings":[{"fix":"Upgrade to Python >=3.8 or pin `pytest-structlog<1.1` in your project's dependencies.","message":"Support for Python 3.7 was dropped in version 1.1. Projects running on Python 3.7 will need to use an older version of `pytest-structlog`.","severity":"breaking","affected_versions":">=1.1"},{"fix":"For complete control, add a `structlog.configure()` call directly in your `conftest.py` and use `--structlog-explicit` (or set `structlog_explicit = true` in `pytest.ini`) to disable automatic processor selection by the plugin.","message":"Prior to v0.4, `pytest-structlog` would reset structlog's defaults, which could interfere with custom `structlog.configure()` setups. While this behavior was reverted, complex structlog configurations (e.g., custom processors) might still require explicit management to avoid conflicts with the plugin's default behavior.","severity":"gotcha","affected_versions":"All versions, but particularly relevant for custom configurations."},{"fix":"Choose either `--structlog-keep` to retain specific processors or `--structlog-evict` to remove them, but do not use both in the same pytest invocation or configuration.","message":"The command-line options `--structlog-keep` and `--structlog-evict` are mutually exclusive. Specifying both will result in an error.","severity":"gotcha","affected_versions":"All versions supporting these options (>=1.0)."},{"fix":"To see `structlog` output during tests, run `pytest` with the `-s` flag (e.g., `pytest -s`) or configure `capture = no` in your `pytest.ini` file.","message":"By default, pytest captures standard output and error, which can hide `structlog` messages printed to the console during test runs, making it seem like no logs are being generated.","severity":"gotcha","affected_versions":"All versions."}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Review your `structlog` and standard library logging configurations in `conftest.py` or `pytest.ini`. Try simplifying the logging setup during tests or investigate specific handlers/processors that might be closing I/O streams prematurely. Sometimes, disabling complex logging configuration in tests or using `pytest -s` can help diagnose the root cause.","cause":"This error can occur when `structlog`'s configuration conflicts with pytest's default capture mechanisms, especially if you are manually configuring `logging.config.dictConfig` with specific handlers or formatters that interfere with `_pytest.capture`.","error":"ValueError: I/O operation on closed file (when running pytest)"},{"fix":"Ensure that `pytest-structlog` is correctly installed and that your `structlog.configure()` call in your application does not prematurely finalize the processor chain in a way that bypasses the plugin's hooks. Verify that the `log` fixture is correctly injected into your test function signature (e.g., `def test_something(log: StructuredLogCapture):`). If using advanced `structlog` configuration, consider using `--structlog-explicit` with your own `structlog.configure()` in `conftest.py`.","cause":"`structlog` configuration might be too aggressive or incorrectly set up to capture logs within the pytest environment, preventing `pytest-structlog` from hooking into the logging pipeline correctly.","error":"pytest does not find tests / No logs captured by 'log' fixture"}]}