8
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.

the
  • 21,007
  • 11
  • 68
  • 101
vv1133
  • 739
  • 1
  • 8
  • 21
  • You have `ch.setLevel(logging.DEBUG)` in your second example, if you use `ch.setLevel(logging.DEBUG)` in your first example you will see the exact same output – Padraic Cunningham Sep 11 '15 at 14:40

1 Answers1

12

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.

jtlz2
  • 7,700
  • 9
  • 64
  • 114
Jeremy Allen
  • 6,434
  • 2
  • 26
  • 31