structlog comes with tools for testing the logging behavior of your application.

If you need functionality similar to unittest.TestCase.assertLogs, or you want to capture all logs for some other reason, you can use the structlog.testing.capture_logs context manager:

>>> from structlog import get_logger
>>> from structlog.testing import capture_logs
>>> with capture_logs() as cap_logs:
...    get_logger().bind(x="y").info("hello")
>>> cap_logs
[{'x': 'y', 'event': 'hello', 'log_level': 'info'}]

Note that inside the context manager all configured processors are disabled.


capture_logs() relies on changing the configuration. If you have cache_logger_on_first_use enabled for performance, any cached loggers will not be affected, so it’s recommended you do not enable it during tests.

You can build your own helpers using structlog.testing.LogCapture. For example a pytest fixture to capture log output could look like this:

def fixture_log_output():
    return LogCapture()

def fixture_configure_structlog(log_output):

def test_my_stuff(log_output):
    assert log_output.entries == [...]

You can also use structlog.testing.CapturingLogger (directly, or via CapturingLoggerFactory that always returns the same logger) that is more low-level and great for unit tests:

>>> import structlog
>>> cf = structlog.testing.CapturingLoggerFactory()
>>> structlog.configure(logger_factory=cf, processors=[structlog.processors.JSONRenderer()])
>>> log = get_logger()
>>> log.info("test!")
>>> cf.logger.calls
[CapturedCall(method_name='info', args=('{"event": "test!"}',), kwargs={})]

Additionally structlog also ships with a logger that just returns whatever it gets passed into it: structlog.testing.ReturnLogger.

>>> from structlog import ReturnLogger
>>> ReturnLogger().msg(42) == 42
>>> obj = ["hi"]
>>> ReturnLogger().msg(obj) is obj
>>> ReturnLogger().msg("hello", when="again")
(('hello',), {'when': 'again'})