pytest-structlog
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.
Common errors
-
ValueError: I/O operation on closed file (when running pytest)
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`.fixReview 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. -
pytest does not find tests / No logs captured by 'log' fixture
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.fixEnsure 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`.
Warnings
- breaking 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`.
- gotcha 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.
- gotcha The command-line options `--structlog-keep` and `--structlog-evict` are mutually exclusive. Specifying both will result in an error.
- gotcha 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.
Install
-
pip install pytest-structlog
Imports
- StructuredLogCapture
from pytest_structlog import StructuredLogCapture
- get_logger
import structlog; logger = structlog.get_logger()
Quickstart
import structlog
from pytest_structlog import StructuredLogCapture
# your_lib.py (or code under test)
logger = structlog.get_logger()
def do_something():
logger.info("Starting process", task_id="abc-123")
for i in range(2):
logger.debug("Step complete", step=i)
logger.warning("Process finished with warnings", result="partial", count=2)
# test_your_lib.py
def test_do_something(log: StructuredLogCapture):
assert len(log.events) == 0 # No logs captured initially
do_something()
# Assert on specific event counts
assert log.count("Starting process", level="info") == 1
assert log.count("Step complete", level="debug") == 2
# Assert on individual events with context
assert log.has("Starting process", task_id="abc-123")
assert log.has("Step complete", step=0)
assert log.has("Step complete", step=1)
assert log.has("Process finished with warnings", result="partial", count=2, level="warning")
# You can also inspect all captured events directly
expected_events = [
log.info("Starting process", task_id="abc-123"),
log.debug("Step complete", step=0),
log.debug("Step complete", step=1),
log.warning("Process finished with warnings", result="partial", count=2)
]
assert log.events == expected_events