Bruce Alport
Bruce Alport

Reputation: 442

Python logging from library module

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

Upvotes: 13

Views: 11910

Answers (1)

Corley Brigman
Corley Brigman

Reputation: 12371

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.

Upvotes: 9

Related Questions