13

I am trying to setup generic logging in my Python library modules so that they use the logger configured in the calling module without having to hard code a logger name. I've seen plenty of examples using logging.basicConfig() in the calling module and calling logging.debug() in the library module, but it doesn't work when I configure my own StreamHandler. What am I doing wrong?

This is my calling module:

import logging, logging.handlers
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
consolehandler = logging.StreamHandler()
consoleformatter = logging.Formatter('[%(name)s] %(levelname)s %(message)s')
consolehandler.setFormatter(consoleformatter)
logger.addHandler(consolehandler)
import libmodule

def run():
    logger.info("message from calling module")
    libmodule.say("message from library module")

I've tried this in libmodule.py:

import logging

def say(msg):
    logging.info(msg)

and this:

import logging
logger = logging.getLogger()

def say(msg):
    logger.info(msg)

But in both cases when I call run(), I get this output:

[callingmodule] INFO message from calling module

but was expecting this:

[callingmodule] INFO message from calling module
[callingmodule] INFO message from library module

1 Answer 1

9

That's because in the first instance, you get a logger by name, and in the second, you just use getLogger(), which returns the root logger. Which is not the same one, and doesn't have your custom stream handler installed.

The easier way to do this is to follow the guidance in the docs here:

'Multiple calls to getLogger() with the same name will always return a reference to the same Logger object.'

In other words, if you just use getLogger('my_common_name') in both places, then both of them will automatically point to the same logger, even if you've customized it in one place.

Added: If you want to capture the logger from a parent, then you would do something like this in your library:

# In libmodule

_log = None

def initLogger(logger=None):
    global _log
    if logger != None:
        _log = logger
    else:
        import logging
        _log = logging.getLogger()

# Later in the module
def say(message):
    _log.info(message)

then in your parent module:

# In the parent module
mylog = a logger i set up

import libmodule
libmodule.initLogger(mylog)

That should make them both point to the same logger.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for responding Corley. That would mean hard coding the common name in my library code and would only work if the user of the library chose the same common name.
well, you are doing that now, sort of (both are looking at the central logging module and looking for hard-code loggers, one is __file__ and the other is the root; neither is looking directly at each other). are you trying to allow a user to reroute the library logger into their own? the standard way to do that is have, say, an initLogger() function to initialize the library logger, that you can pass a logger into. if no logger passed in, it creates its own. something like that.
You're right in saying I want to reroute the library logger into the users configured logger, but I was hoping to do it automatically. The initLogger() approach sounds interesting. Is there an example you can point me at?
added a simple example.
Perfect! Thanks Corley.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.