Delgan / loguru

Python logging made (stupidly) simple
MIT License
19.38k stars 690 forks source link

Loguru writing exception logs in addition to custom (Rich) handler #1172

Open ryan-umbra opened 1 month ago

ryan-umbra commented 1 month ago

Hello Loguru team,

I encountered a bug when working with Rich logging in loguru. I am unsure if this is an issue specific to Rich or other custom handlers as well. I have looked all over and am surprised to not find others with this issue. The issue is that when I configure a Rich logging handler in loguru, then my program catches and logs an exception, the exception gets logged twice: once by what appears to be a plain loguru handler, and once by Rich.

The quickest way to replicate this:

from rich.logging import RichHandler
from loguru import logger

logger.remove()
# All options in here are just to simplify the output messages
logger.add(RichHandler(show_level=False), format="{level} {message}", backtrace=False)

def foo():
    logger.info("hi")
    try:
        raise ValueError("OH NO")
    except ValueError as e:
        logger.exception(f"Caught: {e}")
        raise e
    logger.info("bye")

foo()

The above code creates these logs in the console (I just copied and pasted them into this file, they were not written by a file sink): foo-log.log

I am using loguru 0.7.2 and rich 12.6.0.

The closest issue I could find was this but that was because they weren't removing the default logger, this seems like an issue with logger.exception() explicitly. Thanks in advance for any time and support!

Delgan commented 1 month ago

Hi.

Basically, your log message is formatted twice: once by Loguru then by the Rich handler. Both add the traceback to the formatted message, which is why you see it being duplicated in the output.

You can remove the traceback generated by Loguru by providing a function to the format parameter instead of a string (the {exception} field is otherwise automatically appended for convenience):

logger.add(RichHandler(show_level=False), format=lambda _: "{level} {message}", backtrace=False)