How do I remove a handler from a loguru.logger when using pytest?

Question:

I wrote a Thing class that does logging using loguru. At the bottom of the class file I add a handler to the logger. This is what thing.py looks like.

from loguru import logger


class Thing:
    def __init__(self):
        logger.info("Thing created")

    def __enter__(self):
        logger.info("Thing entered")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        logger.info("Thing exited")


logger.add("thing.log")

if __name__ == "__main__":
    with Thing() as thing:
        logger.info("In with block")

This works fine and it logs to thing.log as expected. What I would like to achieve is that it does not add the handler to thing.log when running tests.

This is my test file:

import pytest
from loguru import logger
from thing import Thing


@pytest.fixture
def thing(mocker):
    mocker.patch("thing.logger", logger)
    with Thing() as thing:
        yield thing


def test_thing(thing, mocker):
    mocker.patch("thing.logger", logger)
    logger.info("In test")
    assert isinstance(thing, Thing)

Now this test passes, but the logs are still written to thing.log (instead to only stdout, which is the default in for a loguru.logger).

How do I make sure that it only logs to the basic loguru.logger when running pytest?

What I tried:

  • Using monkeypatch instead of using mocker: monkeypatch.setattr("thing.logger", logger)
  • Patching in only one place (either in the fixture or in the test function)
  • Patching without replacement: mocker.patch("thing.logger") (so without a replacement logger)
Asked By: Lewistrick

||

Answers:

Remove logger.add("thing.log") from thing.py!

You can either specify (as said in the docs) that you want to log to stdout: logger.add(sys.stdout) or just leave it out because the default for loguru.logger is in fact stdout!

The example provided in their docs:

logger.add(sys.stdout, format="{time} - {level} - {message}", filter="sub.module")

EDIT:

if __name__ == "__main__":
    logger.add("thing.log")

if __name__ == "__main__":
    with Thing() as thing:
        #...

Now the logger will log to thing.log when the module is executed directly, but it will NOT add the file handler when the module is imported by another module (e.g. a test file).

Or you can use logger.remove(0) to stop logging when calling thing(mocker)!

Answered By: J. M. Arnold