Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why does python logging level in basicConfig have no effect?

Tags:

python

logging

import logging

# root logger
root = logging.getLogger()      # root
ch = logging.StreamHandler()
ch.setLevel(logging.WARN)
formatter = logging.Formatter('[root] %(levelname)s - %(message)s')
ch.setFormatter(formatter)
root.addHandler(ch)

# logging as child
c = logging.getLogger('mod')
c.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('[mod] - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
c.addHandler(ch)

c.error('foo')
c.warning('foo')
c.info('foo')
c.debug('foo')

output:

[mod] - ERROR - foo
[root] ERROR - foo
[mod] - WARNING - foo
[root] WARNING - foo
[mod] - INFO - foo
[mod] - DEBUG - foo

It's OK. Level of root is WARN, so INFO and DEBUG of root is not printed. But when I use basicConfig:

import logging

# config root logger
logging.basicConfig(level=logging.WARN, format='[root] %(levelname)s - %(message)s')

# logging as child
c = logging.getLogger('mod')
c.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('[mod] - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
c.addHandler(ch)

c.error('foo')
c.warning('foo')
c.info('foo')
c.debug('foo')

output:

[mod] - ERROR - foo
[root] ERROR - foo
[mod] - WARNING - foo
[root] WARNING - foo
[mod] - INFO - foo
[root] INFO - foo
[mod] - DEBUG - foo
[root] DEBUG - foo

The level of basicConfig is WARN, why level INFO and DEBUG of root can be printed?

And when I use logging.info, it effects.

like image 540
vv1133 Avatar asked Sep 09 '15 06:09

vv1133


1 Answers

You are seeing those [root] info and debug messages because your call to logging.basicConfig creates a root Handler with a level of NOTSET. A handler with a level of NOTSET will output any message it receives (see Handler.setLevel).

>>> import logging
>>> logging.basicConfig(level=logging.WARN, format='[root] %(levelname)s - %(message)s')
>>> [handler.level == logging.NOTSET for handler in logging.getLogger().handlers]
[True]

This differs from your first example because in your first example you are creating a root handler with a level of WARN.

The level= parameter for logging.basicConfig is used to set the level of the root Logger not any root Handler.

Log message propagation

Log messages are propagated up to parent Loggers but the level of any parent Loggers is not considered. It is the level of any Handlers that decides what gets "outputted".

From the docs for logging.Logger.propagate:

Messages are passed directly to the ancestor loggers’ handlers - neither the level nor filters of the ancestor loggers in question are considered.

What is Logger.level for then?

A Logger uses its level to decide if to propagate a message to its and any parent loggers' handlers.

If a Logger does not have a level set then it asks its ancestor Loggers for their level and uses that (see logging.Logger.setLevel).

So, the root logger's level is only relevant if you have not set the level on your child logger.

like image 142
Jeremy Allen Avatar answered Sep 20 '22 21:09

Jeremy Allen