Steve Lorimer
Steve Lorimer

Reputation: 28659

logging in a module: No handlers could be found for logger

I am trying to enable logging in a module using a logger configured in an executable.

According to the documentation,

A good convention to use when naming loggers is to use a module-level logger, in each module which uses logging, named as follows:

logger = logging.getLogger(__name__)

I have followed that, creating a module-level logger in my foo module:

module foo:

import logging

logger = logging.getLogger(__name__)

class Foo():
    def __init__(self):
        logger.info("hello world")

Again, according to the documentation

Creating loggers, handlers, and formatters explicitly using Python code that calls the configuration methods listed above.

In my executable I import foo and set up the logger, basically a copy-paste of the example int he documentation link above:

executable:

import logging
from foo import foo

if __name__ == '__main__':

    # create logger
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)

    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)

    # add ch to logger
    logger.addHandler(ch)

    logger.info('start')

    f = foo.Foo()

I see the log statement from my executable, but my module based logger doesn't work:

start
No handlers could be found for logger "foo.foo"

What am I doing wrong?

Edit:

I tried using basicConfig in my executable, which, according to the documentation does the following:

Does basic configuration for the logging system by creating a StreamHandler with a default Formatter and adding it to the root logger

executable:

import logging
from foo import foo

if __name__ == '__main__':

    # create logger
    logger = logging.getLogger(__name__)
    ... (same as above) ...

    logging.basicConfig(level=logging.DEBUG)

    logger.info('start')

    f = foo.Foo()

And now logging in my module works!

What confuses me is that in my previous example is I am creating a StreamHandler and adding it to the root logger.

So I guess my question should be:

What is basicConfig doing which explicit calls to the logging configuration api isn't?

Upvotes: 1

Views: 6214

Answers (2)

VoteCoffee
VoteCoffee

Reputation: 5107

You need to configure the root logger before you import other modules which will use it as a factory to make their local logger instances.

Change your executable to configure the root logger before getting child loggers

import logging
# root logger must be configured before loading child modules which use it
#logging.basicConfig(level=logging.DEBUG)
root_logger = logging.getLogger()
# create console handler and set level to debug
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
console_format = '%(asctime)15s %(levelname)-8s [%(filename)s:%(lineno)s %(funcName)s] %(message)s'
console_formatter = logging.Formatter(console_format)
console.setFormatter(console_formatter)
root_logger.addHandler(console)

from foo import foo

# create module specific logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

if __name__ == '__main__':

    logger.info('start')

    f = foo.Foo()

Then your child module can be like this:

import logging

# create module specific logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

class Foo():
    def __init__(self):
        logger.info("hello world")

Upvotes: 0

Chen A.
Chen A.

Reputation: 11280

You need to assign handlers to the Foo module logger as well, since they use different logger objects:

import logging

logger = logging.getLogger(__name__)
sh = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
logger.addHandler(sh)

class Foo():
    def __init__(self):
        logger.info("hello world")

==================== RESTART: C:\Python27\tests\test2.py ====================
start
hello world

Or you can use basicConfig function from the logging module to change the defaults of the root logger and new ones:

if __name__ == '__main__':
    sh = logging.StreamHandler()
    logging.basicConfig(level=logging.DEBUG)
    root = logging.getLogger(__name__)
    root.info('start')
    f = Foo()

A great resource for logging - Python Logging Cookbook


Update according to the new question:

Does basic configuration for the logging system by creating a StreamHandler with a default Formatter and adding it to the root logger. The functions debug(), info(), warning(), error() and critical() will call basicConfig() automatically if no handlers are defined for the root logger.

In your code, each module has it's own logger object. You need to specify handlers and configurations for each. The basic config basically sets some defaults for you - in case you don't explicity state them. All logger objects are derived from the root logger, so you change the defaults for the root logger and then new loggers derives it.

Upvotes: 1

Related Questions