Help designing an integration with `logot`
etianen opened this issue ยท comments
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.
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 @overload
would ensure good typing. ๐ช
The alternative is a whole Logot
subclass just for loguru
, which seems heavyweight and unpleasant. ๐คฎ
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 {}")
)
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! โค๏ธ
Hey @etianen.
Great project, and thanks for considering integration with Loguru! ๐
Somewhat related, here is documentation explaining how to capture Loguru logs during tests:
As per the above, that means you could have your default logged
API directly compatible with Loguru (although a Loguru-specific handler would still need to be installed). Now, as you suggested, you could also have logged
that offers a tailored API for Loguru.
To be honest, I'm unsure whether %s
or {}
would be preferable for parsing Loguru logs. As you mentioned, using %s
would likely resonate well with C developers due to its resemblance to scanf()
syntax. Additionally, it would maintain a consistent API of your library across standard logging
and loguru
. Conversely, opting for {}
aligns better with Loguru's format. It would probably make the logged
usage more straightforward, especially when dealing with parsing f-strings.
Both approaches look fine to me. Not sure I'm of great help here by saying that. ๐
In any case, that certainly won't be "unappealing". I like the idea of using placeholders to replace regex patterns (whether it uses %
or {}
syntax).
Thanks for getting back so quickly!
I'm surprised (but pleased) you don't see the %s
message match syntax as a barrier to adoption. I'll probably leave this out of the initial integration and leave it in the issue tracker as a possible enhancement depending on the popularity of the library (and the loguru
integration!).
Thanks for those docs links - I'd already read them to make sure a loguru
integration was actually possible, and it was nice to see that it would be easy via the already-implemented logging handler.
I'm happy to close this now, as you've answered all my questions and I'm now enthused about the integration! ๐ I'll post a small update here once the integration is in place to see if you have any thoughts.
It finally landed! https://logot.readthedocs.io/latest/integrations/loguru.html ๐
The loguru
integration was far more straightforward than stdlib logging
, with no footguns. I found that pretty surprising, since I've used logging
for a very long time and only really dabbled with loguru
. โค๏ธ
For comparison:
logging
integration ๐ฅตloguru
integration ๐
I'm hoping to promote logot
as a way to test logs irrespective of testing framework, logging framework (or async framework!). I'm hoping logot
can be a more powerful caplog
/ assertLogs
fixture that doesn't care if you're using logging
, loguru
, structlog
, or anything else.
Would you accept a PR linking to logot
from your help and guides section? Nothing huge, and I'd obviously take your guidance on where to put it / how to phrase it ๐ (and I understand completely if you'd rather not!).
Congrats on the release, @etianen! I'm glad you found integration with Loguru easy!
Your library would definitely fit well in the Loguru documentation. I'd be happy to accept a PR. ;)
This could be covered in one of the two sections I linked to above, or we could create a new section in Code Snippets and Recipes for Loguru to explain how to test logging. If the latter, we should probably first give general guidance, possibly by pointing to the other sections mentioned earlier, then suggest usage of specialized and more convenient libraries such as Logot.
I have no particular preference. Choose what you think is best, I'm confident it will be great. ๐