问题
As the logger.setLevel doc says:
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 I think if I create a root logger, with level NOTSET, the debug and info log will display.
The code use basicConfig to set root logger's level to NOTSET is right:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
logging.basicConfig(level=logging.NOTSET)
log = logging.getLogger()
log.debug('debug')
log.info('info')
log.warning('warning')
log.error('error')
log.critical('critical')
and output is:
DEBUG:root:debug
INFO:root:info
WARNING:root:warning
ERROR:root:error
CRITICAL:root:critical
But if I create a root logger, and add handler with NOTSET level to it, such as:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
log = logging.getLogger()
hd = logging.StreamHandler()
hd.setLevel(logging.NOTSET)
log.addHandler(hd)
log.debug('debug')
log.info('info')
log.warning('warning')
log.error('error')
log.critical('critical')
the output is:
warning
error
critical
but I think it will also output the debug and info message.
回答1:
OK, I misunderstand the Logger's level
and Handler's Level
, in the doc:
The setLevel() method, just as in logger objects, specifies the lowest severity that will be dispatched to the appropriate destination. Why are there two setLevel() methods? The level set in the logger determines which severity of messages it will pass to its handlers. The level set in each handler determines which messages that handler will send on.
If I change code to this, will be ok:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
log = logging.getLogger()
log.setLevel(logging.NOTSET) # Set Logger's level to NOTSET, default is WARNING
#print "Logger's Level: ", log.level
hd = logging.StreamHandler()
hd.setLevel(logging.NOTSET)
#print "Handler's Level: ", hd.level
log.addHandler(hd)
log.debug('debug')
log.info('info')
log.warning('warning')
log.error('error')
log.critical('critical')
回答2:
Let's explain by example that UNSET does NOT do anything when creating concrete instances of loggers (for some mysterious reason):
def logging_unset_level():
"""My sample logger explaining UNSET level
Resources:
- https://stackoverflow.com/questions/21494468/about-notset-in-python-logging
- https://www.youtube.com/watch?v=jxmzY9soFXg&t=468s
- https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced
"""
import logging
logger = logging.getLogger(__name__) # loggers are created in hierarchy using dot notation, thus __name__ ensures no name collisions.
print(f'DEFAULT VALUE: logger.level = {logger.level}')
file_handler = logging.FileHandler(filename='my_log.log')
log_format = "{asctime}:{levelname}:{lineno}:{name}:{message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
formatter = logging.Formatter(fmt=log_format, style='{')
file_handler.setFormatter(fmt=formatter)
stdout_stream_handler = logging.StreamHandler(stream=sys.stdout)
stdout_stream_handler.setLevel(logging.INFO)
log_format = "{name}:{levelname}:-> {message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
formatter = logging.Formatter(fmt=log_format, style='{')
stdout_stream_handler.setFormatter(fmt=formatter)
logger.addHandler(hdlr=file_handler)
logger.addHandler(hdlr=stdout_stream_handler)
logger.log(logging.NOTSET, 'notset')
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.critical('critical')
the output to screen is:
DEFAULT VALUE: logger.level = 0
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical
the output to stdout
is:
2020-04-15 17:00:38,384:WARNING:200:__main__:warning
2020-04-15 17:00:38,384:ERROR:201:__main__:error
2020-04-15 17:00:38,384:CRITICAL:202:__main__:critical
as expected from the question's body, when the logger is instantiated its setting is UNSET
. If you are using the basic config for some reason it does work as the docs imply it should everything from UNSET to higher get logged BUT when instantiating loggers concretely this setting makes only error messages work for some reason (find me a reference!).
Ok, now if you run it again but increase the level of the top logger everything works as you would expect (except the unset):
logger.setLevel(logging.DEBUG)
The output of stdout (note the stdout handler has level INFO in my example for pedagogical reasons, different from the file handler, trying to show inheritance of the .level value from the top logger):
DEFAULT VALUE: logger.level = 10
__main__:INFO:-> info
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical
and the output of the logger file my_log.log
(this one DOES output the debug statements! :D ):
2020-04-15 17:05:58,782:DEBUG:198:__main__:debug
2020-04-15 17:05:58,784:INFO:199:__main__:info
2020-04-15 17:05:58,784:WARNING:200:__main__:warning
2020-04-15 17:05:58,784:ERROR:201:__main__:error
2020-04-15 17:05:58,784:CRITICAL:202:__main__:critica
is as expected. It inherited the top level loggers level so it printed everything from DEBUG level upwards.
来源:https://stackoverflow.com/questions/21494468/about-notset-in-python-logging