Delgan / loguru

Python logging made (stupidly) simple
MIT License
19.69k stars 693 forks source link

my logger stops logging to one file but switches to another??? #719

Open xFGhoul opened 2 years ago

xFGhoul commented 2 years ago

I have 2 loggers, one for my main thread, and the other is a logger for a class, the config dict looks something like this:

LOGGING_CONFIG: Final[Dict] = {
     "handlers":  [
        {
          "sink": LOGGING_PATH,
          "format": "[{time:YYYY-MM-DD HH:mm:ss}] {module}::{function}({line}) - {message}",
          "enqueue": True,
           "rotation": "daily",
          "level": "INFO",
          "serialize": False,
          "backtrace": False,
          "catch": False,
          },
       ],
}

now in my main code i do something like:

from loguru import logger

logger.configure(**LOGGING_CONFIG)

logger.info("blah blah blah") # works all good

AClass = SomeClass(...) # this has its own LOGGING_CONFIG
other_logger = Thread(name="....", target=AClass.some_method)
other_logger.start()

logger.info("blah blah blah v2")

now the last logger.info("blah blah blah v2") will log to other_logger's config setup, and not the one set up, thats its supposed to first, why? and how do I fix this?

Delgan commented 2 years ago

In Loguru, there is only one logger object. Every logged message will pass through all handlers added so far, so you may need to filter them. See Creating independent loggers with separate set of handlers.

xFGhoul commented 2 years ago

I don't understand, these examples aren't really providing me what I'm looking for

xFGhoul commented 2 years ago

and if I was to be more specific:

if __name__ == "__main__":
    logger.configure(**LOGGING_CONFIG)

    logger.info("this actually goes through the 'sink' i want")

    # -- Initialize Thread
    logger.info("Initializng Thread") # -- This still works
    THREAD = CLASS(...)

    Thread = Thread(name="Thread", target=THREAD.start)
    Thread.start()
    logger.info("Thread Successfully Started") # -- this now logs what THREAD.start started logging, even though the 'sinks' which is what I assume is the file chosen to be logged too
xFGhoul commented 2 years ago

if there is only one logger object, why is it not noticing that there are 2 different sinks? im not using logger.add, is that something I need to do?

Delgan commented 2 years ago

Can you please provide a fully reproducible example with the expected output? Sorry but I have trouble understanding the issue you're facing.

You don't need to call logger.add() because you used logger.configure() which itself will add the handlers internally (after removing the existing ones). Based on your LOGGING_CONFIG dict, only one handler will be added (logging to a file).

xFGhoul commented 2 years ago

The issue is that I have two loggers essentially:

  1. main thread
  2. code running in a seperate thread

when my main program starts, it calls logger.configure() with this config:

    LOGGING_CONFIG: Final[Dict] = {
            "handlers": [
                {
                    "sink": LOGGING_PATH,
                    "format": "[{time:YYYY-MM-DD HH:mm:ss}] {module}::{name}({line}) - {message}",
                    "enqueue": True,
                    "mode": "w",
                    "rotation": "daily",
                    "level": "INFO",
                    "serialize": False,
                    "backtrace": False,
                    "catch": False,
                },
            ],
        }

now as my program runs it goes smoothly, logging exactly to what LOGGING_PATH file it should be logged too, then as I initialize a core thread that needs to be run:

ImportantClass = AnImportantClass(debug=True, logs_path=..., webhook_url=...)

ImportantThread = Thread(name="Something Important", target=ImportantClass.start)
ImportantThread.start()

now, in the __init__ of AnImportantClass, it also calls logger.configure() with this config:

        LOGGING_CONFIG: Dict = {
            "handlers": [
                {
                    "sink": self.logs_path,
                    "format": "[{time:YYYY-MM-DD HH:mm:ss}] {module}::{function}({line}) - {message}",
                    "enqueue": True,
                    "rotation": "daily",
                    "level": "INFO",
                    "mode": "w",
                    "serialize": False,
                    "backtrace": False,
                    "catch": False,
                },
            ],
        }

now when this gets called, instantly what the first program LOGGING_PATH that was being written too starts being written to self.logs_path, so my question is why does the two logging paths get conflicted

and yes, I have tested not initializing this thread and it does work correctly so I have pin pointed the issue down to the __init__ method, now my theory is that it overwrites the original logger.configure() like you mentioned, so I guess my question is now how do I instead use both loggers at the same time?

xFGhoul commented 2 years ago

@Delgan any update on my issue?

Delgan commented 2 years ago

You can use .add() instead of .configure(), it won't remove the existing handlers.

xFGhoul commented 1 year ago

You can use .add() instead of .configure(), it won't remove the existing handlers.

.add() does nothing

xFGhoul commented 1 year ago

it also starts printing logging to stdout

Delgan commented 1 year ago

I think you're confusion comes from the fact that there is only one logger but two handlers, from the way Loguru works. All logged messages will go through all added handlers if they are not filtered.

Can you please clarify what is the expected output of your configuration? Does LOGGING_PATH and self.logs_path represent the same file?

xFGhoul commented 1 year ago

I think you're confusion comes from the fact that there is only one logger but two handlers, from the way Loguru works. All logged messages will go through all added handlers if they are not filtered.

Can you please clarify what is the expected output of your configuration? Does LOGGING_PATH and self.logs_path represent the same file?

the expected output is that

  1. i initalize my main logger
  2. an seperate module initializes its own logger (with a different sink)
  3. tthey both log to their seperate files

no, self.logs_path is part of ImportantClass own constructor, LOGGING_PATH is my "main" logger

to put it simply, the issue is that:

  1. i start logging to a.txt
  2. i initialize a logger that should log to b.txt
  3. what was supposed to be logged to a.txt now routes to b.txt
Delgan commented 1 year ago

Thanks for the details.

It's pretty clear now that you'll need to use a custom filter to create independent loggers with separate set of handlers.

To summarize and explain the issues you faced:

For this reason, you need a way to select which logs go to which handler. This is usually done using custom filter and .bind():

from loguru import logger

logger_a = logger.bind(name="a")
logger.add("a.txt", filter=lambda r: r["extra"].get("name") == "a")

logger_b = logger.bind(name="b")
logger.add("b.txt", filter=lambda r: r["extra"].get("name") == "b")

logger_a.info("Logged to 'a.txt' only")
logger_b.info("Logged to 'b.txt' only")
logger.info("Not logged to any file")