Delgan/loguru

How to preserve the default sink when calling .remove()? How to remove all but the default sync when calling .remove()

Closed this issue · 4 comments

Sometimes an example is easier than an abstract description.

Starting with this simple program:

from loguru import logger


import sys

word = sys.argv[1]

log_file = f"{word}" + "_{time}" + ".log"
logger.add(log_file)

logger.debug(word)

If we run this program via python program.py hello and then run the
program again via python program.py world we get 2 log files, each
with exactly 1 line. 1 file will look like this:

2020-12-10 11:47:42.348 | DEBUG    | __main__:<module>:12 - hello

and the other file like this:
2020-12-10 11:47:47.179 | DEBUG | main::12 - world

However, that is not what happens in our Python Spark environment

In our spark environment, successive invocations of the sample script
lead to the second log file having all the output of the first run and
the second:

2020-12-10 11:47:42.348 | DEBUG    | __main__:<module>:12 - hello
2020-12-10 11:47:47.179 | DEBUG    | __main__:<module>:12 - world

And then the 3rd run having the output of the 1st 2 runs in addition
to it's own. And so on and so forth.

My fix was to use .remove()

So I modified our script, similar the sample script, as follows:

import sys
from loguru import logger

word = sys.argv[1]

log_file = f"{word}" + "_{time}" + ".log"
logger.remove()
logger.add(log_file)

logger.debug(word)

In other words, I called .remove() prior to adding the new filename,
so that it would not use previously created sinks.

But I only wanted to remove file sinks, not the default sys.stderr sink

So:

  1. how would I call .remove() so that it does not remove the
    default sink?
  2. Since there is no such documented option for .remove() perhaps
    something is in place like getting a list of handler ids? I do not
    want to keep track of created handler ids via some storage
    mechanism. I would much rather a call like logger.handlers() and if
    the length if greater than 1, then I remove all but the zeroeth element.

Well, it's not really possible to selectively remove the handlers. You need to store the id returned by logger.add() somewhere so you can pass it to logger.remove() later.

However in your case, I may see a simpler solution. Why not just add a stderr sink again? This is exactly the same as the default handler.

from loguru import logger


import sys

word = sys.argv[1]

log_file = f"{word}" + "_{time}" + ".log"
logger.add(log_file)
logger.add(sys.stderr)

logger.debug(word)

You need to store the id returned by logger.add() somewhere so you can pass it to logger.remove() later.

I see. How do you feel about a logger.handlers attribute/method that returns a list of all handlers so that one can do;

for i, handler in enumerate(logger.handlers):
    if i == 0: # default handler
        continue
    else:
        logger.remove(handler.id)

But thank you for the speedy reply. I i'm sure your suggestion will work for me.

This is a suggestion that comes up from time to time. However I prefer to avoid this solution because it requires to expose the internal "handler" object. This handler has no public interface, it is essentially immutable. Using Loguru, the configuration is only done through the logger object. I don't have a big enough reason to give access to the handler, and I prefer to keep the API minimalist.

I'm closing this issue. As discussed, I prefer not to expose the handlers.