Mayil-AI / loguru

MIT License
0 stars 0 forks source link

Help designing an integration with `logot` (hashtag1072) #7

Open vikramsubramanian opened 3 months ago

vikramsubramanian commented 3 months ago

I've recently released a 0.1.0 preview of a log testing library called [logot]( It provides a nice fixture for testing [complex log patterns]( which I've found very useful when testing multithreaded and async codebases.

I'd like to provide a loguru integration. 🤗Since I'm not hugely familiar with loguru, I was hoping you'd have some advice on what a good integration would look like.

There are a couple of sticking points that I'd like to nail before committing to a v1.0.0 API.

hashtag Log capture

Currently, stdlib logging capture is enabled with a [context manager method](

from logot import Logot

with Logot().capturing() as logot:
    pass

Since the loguru integration would be an optional extra, a freestanding context manager could be provided like this:

from logot.contrib.loguru import capturing

with capturing(Logot()) as logot:
   ...

It's a bit awkward having a freestanding context manager for 3rd party integrations, so an alternative would be a capturing backend approach:

from logot.contrib.loguru import LoguruCapture

with Logot().capturing(LoguruCapture):
   pass

Any arguments given to capturing would be propagated to the LoguruCapture constructor. This would then unify capture between stdlib logging and loguru logging. The default capture backend would be the stdlib capture, and an ` would ensure good typing. 💪

The alternative is a whole Logot subclass just for loguru, which seems heavyweight and unpleasant. 🤮

hashtaghashtag Log message matching

Rather than awkward regexes, logot lets you [match log messages]( with % placeholders. The hope here is that it's much cleaner and more intuitive to match using the syntax used for log message placeholders. (Similar to how printf and scanf pair nicely).

from logot import logged

logot.wait_for(logged.info("Hello %s"))

But loguru uses lovely .format()-style placeholders!

Maybe it's fine to just use % placeholders in loguru tests? 🤷

But it might be nice to support .format()-style placeholders like this:

from logot.contrib.loguru import logged

logot.wait_for(logged.info("Hello {}"))

Support for matching structured data would also be possible:

logot.wait_for(
    logged.bind(foo="bar").info("Hello {}")
)

hashtaghashtag Advice appreciated! 🙏

Thanks for reading this far!

I'm inclined to go with a LoguruCapture backend combined with .format()-style message matching. That would give first-class support for loguru without compromising or complicating the stdlib interface.

However, I'm not really familiar with the loguru community and ecosystem! Maybe this is unappealing? Maybe it's already been done! 😬

Hope you have a nice weekend! ❤️ )

mayil-ai[bot] commented 3 months ago

Based on the ticket description, here are the recommendations for integrating logot with loguru:

Here is a high-level overview of the proposed changes in pseudo-code:

# logot/contrib/loguru_integration.py

from loguru import logger
from logot import Logot

class LoguruCapture:
    def __init__(self, *args, **kwargs):
        # Initialize with any arguments passed to Logot().capturing()
        pass

    def start_capture(self):
        # Implement logic to start capturing logs from loguru
        pass

    def stop_capture(self):
        # Implement logic to stop capturing logs from loguru
        pass

    # Additional methods as needed for log capture functionality

# Modify the Logot class to accept a capturing backend
class Logot:
    def capturing(self, backend=None):
        if backend is None:
            backend = StdlibCapture # Default to stdlib capture
        capture_backend = backend(*args, **kwargs)
        capture_backend.start_capture()
        try:
            yield self
        finally:
            capture_backend.stop_capture()

# Extend log message matching functionality to support .format() placeholders
class LogotMessageMatcher:
    def wait_for(self, message_pattern):
        # Implement logic to match log messages using .format() placeholders
        pass

# Usage example
from logot.contrib.loguru_integration import LoguruCapture

with Logot().capturing(backend=LoguruCapture) as logot:
    # Perform actions that generate logs
    pass

# Match a log message with .format() placeholders
logot.wait_for("Hello {}")

This approach provides a clean integration with loguru while maintaining the simplicity and usability of the logot library. It also allows for future extensions and additional third-party integrations.