Psionman
Psionman

Reputation: 3677

How can I set the level on a custom python logger?

I have a custom python logger

# logger.py

import logging
#logging.basicConfig(level=logging.DEBUG)

logger = logging.getLogger(__name__)

c_handler = logging.StreamHandler()
c_handler.setLevel(logging.DEBUG)

c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
c_handler.setFormatter(c_format)

logger.addHandler(c_handler)

I have set the level to DEBUG, but only WARNINGS (and above) are shown

from ..logger import logger
...
logger.debug('this is a debug log message')
logger.warning('too hot to handle')
...

my_module.logger:too hot to handle

if I uncomment the line

logging.basicConfig(level=logging.DEBUG)

then I get the DEBUG level, but two copies of the message

my_module.logger - DEBUG - this is a debug log message

DEBUG:my_module.logger:this is a debug log message

my_module.logger - WARNING - too hot to handle

WARNING:my_module.logger:too hot to handle

I am not importing logging at any other point in the package

How should I configure the logger?

Upvotes: 0

Views: 1051

Answers (2)

Psionman
Psionman

Reputation: 3677

Having read the docs again I realise that propagate is the attribute that I need to use to turn off the ancestor logging output. So my logger becomes

# logger.py

import logging
logging.basicConfig(level=logging.DEBUG)
logger.propagate = False

logger = logging.getLogger(__name__)

c_handler = logging.StreamHandler()
c_handler.setLevel(logging.DEBUG)

c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
c_handler.setFormatter(c_format)

logger.addHandler(c_handler)

And I get just one log message and the debug level is used

Upvotes: 0

iBug
iBug

Reputation: 37227

TL;DR Use logger.setLevel(logging.DEBUG)


According to Python documentation, a handler processes messages with a level equal to or higher than the handler is set to (via .setLevel()).

But also note, emphasis mine:

When a logger is created, the level is set to NOTSET (which causes all messages to be processed when the logger is the root logger, or delegation to the parent when the logger is a non-root logger). Note that the root logger is created with level WARNING.

So without logging.basicConfig, there's no "root logger" at program startup, and your first getLogger() creates a stub root logger with default level WARNING, and your logger with level NOTSET (which fallbacks to that of the root logger). As a result your logger.debug message is thrown away before it gets handled.

With logging.basicConfig, you explicitly create a root logger with the given level and a StreamHandler with default Formatter. Your new getLogger() is attached to the root logger and any log record is propagated to the root logger - thus printing twice with a different formatter (the default one indeed).

The stub root logger created by the first call to getLogger() has no handler attached so any propagated record is not printed out.

If you want to have full control over your logging facility, it's better to give your logger an explicit level than relying on basicConfig, which creates a root logger that you may not want:

logger.setLevel(logging.DEBUG)

Upvotes: 1

Related Questions