Delgan / loguru

Python logging made (stupidly) simple
MIT License
18.69k stars 682 forks source link

Traceback in file sink but not in stderr #1119

Closed Kalindro closed 3 months ago

Kalindro commented 3 months ago

Hello,

I love using loguru. I've been wondering if it's possible to get the traceback with the handler that saves to the file but no traceback with handlers that prints to the console? I want to monitor console logs with less clutter but get more details from the file logs.

Let's say I have something like this:

if isinstance(err, (ProxyConnectionError, ProxyTimeoutError, TimeoutError, ConnectionResetError, InvalidMessage)):
    logger.error(f"Regular error: {err}")
else:
    logger.exception(f"New error: {err}")

Here the console will print traceback only for the else case but so will the file handler. I don't think it can be done by just setting regular levels, is it maybe possible with custom levels? Or maybe there is an option that I'm missing that can make it easy?

Delgan commented 3 months ago

Hi @Kalindro.

You can surely achieve what you're looking for by configuring your two handlers differently.

For example, to have less verbose traceback on sys.stderr but all variables in your logged file:

logger.remove()
logger.add(sys.stderr, diagnose=False, backtrace=False)
logger.add("file.log", diagnose=True, backtrace=True)

Now if you want to disable the traceback entirely, you can use a different format for your console logs:

logger.remove()
logger.add(sys.stderr, format=lambda r: "{time} {message}\n")
logger.add("file.log")

By default, the {exception} formatting field is appended to the string-format and will render the traceback. But if format is a function, the {exeception} isn't automatically added, which effectively means no traceback will be formatted.

Other solutions are possible using bind() or patch() for example.

Kalindro commented 3 months ago

Hi @Delgan,

Thank you for the response. I think I need to give bind(), patch() or custom level a spin. As with above approach, if I disable traceback for sys.stderr, I won't have traceback in console when calling exception. I think from the logic standpoint, the sys.stderr handler behavior should be as it's per default (so it will print traceback with exception but not with error) but for file handler it should add traceback even for error. The only idea I have is creating a custom level that does basically the same thing as exception does and changing my python code to something like:

if isinstance(err, (ProxyConnectionError, ProxyTimeoutError, TimeoutError, ConnectionResetError, InvalidMessage)):
    logger.error(f"Error without traceback in console: {err}")
    logger.custom(f"Error with traceback to file: {err}")
else:
    logger.exception(f"Exception with traceback in console: {err}")
    logger.custom(f"Exception with traceback to file: {err}")

And make the file logger only work with the custom level and sys.stderr handler not. But there may be an easier way to achieve this. I would love if this could be done on handler level instead and my code would stay the same but I think it's not doable.

Delgan commented 3 months ago

In such a case, you can use bind() in combination with a custom format:

def formatter(record):
    if "disallow_traceback_in_console" in record["extra"]:
        return "{time} {message}\n"
    else:
        return "{time} {message}\n{exception}"

logger.remove()
logger.add(sys.stderr, format=formatter)
logger.add("file.log")

...

if isinstance(err, (ProxyConnectionError, ProxyTimeoutError, TimeoutError, ConnectionResetError, InvalidMessage)):
    logger.bind(disallow_traceback_in_console=True).exception("Regular error")
else:
    logger.exception("New error")
Kalindro commented 3 months ago

Thas is great, the use of bind with the fact that when format is a function it won't append the exception, haven't thought about it. Thank you a lot!