Pytest-Logging

pytest captures log messages of level WARNING or above automatically
and displays them in their own section for each failed test in the same
manner as captured stdout and stderr.

Running without options:

pytest

Shows failed tests like so:

----------------------- Captured stdlog call ----------------------
test_reporting.py    26 WARNING  text going to logger
----------------------- Captured stdout call ----------------------
text going to stdout
----------------------- Captured stderr call ----------------------
text going to stderr

By default each captured log message shows the module, line number, log
level and message.

If desired the log and date format can be specified to anything that the
logging module supports by passing specific formatting options:

pytest --log-format="%(asctime)s %(levelname)s %(message)s" \
        --log-date-format="%Y-%m-%d %H:%M:%S"

Shows failed tests like so:

----------------------- Captured stdlog call ----------------------
2010-04-10 14:48:44 WARNING text going to logger
----------------------- Captured stdout call ----------------------
text going to stdout
----------------------- Captured stderr call ----------------------
text going to stderr

These options can also be customized through pytest.ini file:

[pytest]
log_format = %(asctime)s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S

Further it is possible to disable reporting of captured content (stdout,
stderr and logs) on failed tests completely with:

pytest --show-capture=no

caplog fixture

Inside tests it is possible to change the log level for the captured log
messages. This is supported by the caplog fixture:

def test_foo(caplog):
    caplog.set_level(logging.INFO)
    pass

By default the level is set on the root logger, however as a convenience
it is also possible to set the log level of any logger:

def test_foo(caplog):
    caplog.set_level(logging.CRITICAL, logger="root.baz")
    pass

The log levels set are restored automatically at the end of the test.

It is also possible to use a context manager to temporarily change the
log level inside a with block:

def test_bar(caplog):
    with caplog.at_level(logging.INFO):
        pass

Again, by default the level of the root logger is affected but the level
of any logger can be changed instead with:

def test_bar(caplog):
    with caplog.at_level(logging.CRITICAL, logger="root.baz"):
        pass

Lastly all the logs sent to the logger during the test run are made
available on the fixture in the form of both the logging.LogRecord
instances and the final log text. This is useful for when you want to
assert on the contents of a message:

def test_baz(caplog):
    func_under_test()
    for record in caplog.records:
        assert record.levelname != "CRITICAL"
    assert "wally" not in caplog.text

For all the available attributes of the log records see the
logging.LogRecord class.

You can also resort to record_tuples if all you want to do is to
ensure, that certain messages have been logged under a given logger name
with a given severity and message:

def test_foo(caplog):
    logging.getLogger().info("boo %s", "arg")

You can call caplog.clear() to reset the captured log records in a
test:

def test_something_with_clearing_records(caplog):
    some_method_that_creates_log_records()
    caplog.clear()
    your_test_method()

The caplog.records attribute contains records from the current stage
only, so inside the setup phase it contains only setup logs, same with
the call and teardown phases.

To access logs from other stages, use the caplog.get_records(when)
method. As an example, if you want to make sure that tests which use a
certain fixture never log any warnings, you can inspect the records for
the setup and call stages during teardown like so:

@pytest.fixture
def window(caplog):
    window = create_window()
    yield window
    for when in ("setup", "call"):
        messages = [
        ]
        if messages:
            pytest.fail(
                "warning messages encountered during testing: {}".format(messages)
            )

The full API is available at
_pytest.logging.LogCaptureFixture{.interpreted-text role=“class”}.

Live Logs {#live_logs}

By setting the log_cli{.interpreted-text role=“confval”} configuration
option to true, pytest will output logging records as they are emitted
directly into the console.

You can specify the logging level for which log records with equal or
higher level are printed to the console by passing --log-cli-level.
This setting accepts the logging level names as seen in python's
documentation or an integer as the logging level num.

Additionally, you can also specify --log-cli-format and
--log-cli-date-format which mirror and default to --log-format and
--log-date-format if not provided, but are applied only to the console
logging handler.

All of the CLI log options can also be set in the configuration INI
file. The option names are:

  • log_cli_level
  • log_cli_format
  • log_cli_date_format

If you need to record the whole test suite logging calls to a file, you
can pass --log-file=/path/to/log/file. This log file is opened in
write mode which means that it will be overwritten at each run tests
session.

You can also specify the logging level for the log file by passing
--log-file-level. This setting accepts the logging level names as seen
in python's documentation(ie, uppercased level names) or an integer as
the logging level num.

Additionally, you can also specify --log-file-format and
--log-file-date-format which are equal to --log-format and
--log-date-format but are applied to the log file logging handler.

All of the log file options can also be set in the configuration INI
file. The option names are:

  • log_file
  • log_file_level
  • log_file_format
  • log_file_date_format

You can call set_log_path() to customize the log_file path
dynamically. This functionality is considered experimental.

Release notes {#log_release_notes}

This feature was introduced as a drop-in replacement for the
pytest-catchlog plugin and
they conflict with each other. The backward compatibility API with
pytest-capturelog has been dropped when this feature was introduced,
so if for that reason you still need pytest-catchlog you can disable
the internal feature by adding to your pytest.ini:

[pytest]
    addopts=-p no:logging

Incompatible changes in pytest 3.4 {#log_changes_3_4}

This feature was introduced in 3.3 and some incompatible changes
have been made in 3.4 after community feedback:

  • Log levels are no longer changed unless explicitly requested by the
    log_level{.interpreted-text role=“confval”} configuration or
    --log-level command-line options. This allows users to configure
    logger objects themselves. Setting log_level{.interpreted-text
    role=“confval”} will set the level that is captured globally so if a
    specific test requires a lower level than this, use the
    caplog.set_level() functionality otherwise that test will be prone
    to failure.
  • Live Logs <live_logs>{.interpreted-text role=“ref”} is now
    disabled by default and can be enabled setting the
    log_cli{.interpreted-text role=“confval”} configuration option to
    true. When enabled, the verbosity is increased so logging for each
    test is visible.
  • Live Logs <live_logs>{.interpreted-text role=“ref”} are now sent
    to sys.stdout and no longer require the -s command-line option
    to work.

If you want to partially restore the logging behavior of version 3.3,
you can add this options to your ini file:

[pytest]
log_cli=true
log_level=NOTSET

More details about the discussion that lead to this changes can be read
in issue #3013.