wimglenn / pytest-structlog

Structured logging assertions
MIT License
54 stars 6 forks source link

There is no way currently to filter bu `logger_name` #22

Closed sshishov closed 8 months ago

sshishov commented 1 year ago

Usually it is okay to just assert that some message in the logs with appropriate level. But it would be great to support the logger name and other metadata there.

assert log.has('my_message', level='error', logger_name='my_logger')

Also it would be great to have a pretty-print for the library to print the diff of dictionaries (as many people are using pytest-icdiff or pytest-clarity which will represent them great dict diff in case of something differs

wimglenn commented 1 year ago

@sshishov Assertions with other metadata/context such as :

assert log.has('my_message', level='error', logger_name='my_logger')

should already be working fine (and here is the test for it). Are you seeing otherwise? Could you make a reproducible example?

For the rich diff, I already see a rich diff when comparing captured events directly with pytest-clarity installed, it looks like this:

Screenshot 2023-04-08 at 8 05 59 PM

(I added "k3": "v3" to make the test fail)

However there is no rich diff for the log.has method, since it's just returning a boolean. I'll have a think about how that might work, although I'm not entirely sure how we could decide what to diff against - because log.has just asserts that one event is present in there, out of possibly hundreds of events logged.

e.g. if we logged

log.info(k1="v1", k2="v2")
log.info(k1="v1", k3="v3")

and then we asserted

assert log.has(k1="v1", k0="v0")

Which event should it be diffing with? The {"k1": "v1", "k2": "v2"} event or the {"k1": "v1", "k3": "v3"} event?

wimglenn commented 8 months ago

If you want the logger name in the context dict, you'll have to add it explicitly because pytest-structlog will not do that by default.

Something like this ought to work:

@pytest.fixture(autouse=True)
def inject_logger_name(log):
    original_processors = structlog.get_config().get("processors", [])
    if structlog.stdlib.add_logger_name not in original_processors:
        processors = [structlog.stdlib.add_logger_name] + original_processors
        log.original_configure(processors=processors)
        yield
        log.original_configure(processors=original_processors)

I've considered retaining a structlog.stdlib.add_logger_name, similar like we do for a positional arguments formatter, but that would be a breaking change. A better way forward (more powerful/flexible and with backwards compatibility) may be to keep the default configuration as-is, but provide some ini options for users of pytest-structlog to override the default processors for the plugin.

sshishov commented 8 months ago

Thanks you for the clarification, @wimglenn , your comment makes sense! And very thank you for your time!

wimglenn commented 3 months ago

@sshishov FYI, in pytest-structlog 1.0 there may be an easier option available, you can run with pytest --structlog-keep add_logger_name or add this in your pyproject.toml configs:

[tool.pytest.ini_options]
structlog_keep = ["add_logger_name"]

If the add_logger_name was already present in config at the time of running tests, it will be kept in the config.

That's hopefully simpler than the inject_logger_name fixture demonstrated in my previous comment (although that approach should continue to work fine).