I am trying to grasp the difference between the impact of logger.setLevel and the impact of handler.setLevel, where the logger and the handler are created as follows:
import logging
def getLogger(name, loggerLevel, handlerLevel):
    formatter = logging.Formatter('%(message)s')
    handler = logging.StreamHandler()
    handler.setLevel(handlerLevel)
    handler.setFormatter(formatter)
    logger = logging.getLogger(name)
    logger.setLevel(loggerLevel)
    logger.addHandler(handler)
    return logger
Note that the handler is connected to the logger.
Here is what the official doc says about setLevel:
Sets the threshold for this logger to
level. Logging messages which are less severe thanlevelwill be ignored; logging messages which have severitylevelor higher will be emitted by whichever handler or handlers service this logger, unless a handler’slevelhas been set to a higher severity level thanlevel.
I didn't quite fully understand this, so I implemented the following script to test all possible combinations:
for loggerLevel in ['DEBUG', 'INFO', 'WARNING', 'ERROR']:
    for handlerLevel in ['DEBUG', 'INFO', 'WARNING', 'ERROR']:
        name = loggerLevel + '_' + handlerLevel
        print('{}:'.format(name))
        logger = getLogger(name,eval('logging.'+loggerLevel),eval('logging.'+handlerLevel))
        logger.debug  ('    debug  ')
        logger.info   ('    info   ')
        logger.warning('    warning')
        logger.error  ('    error  ')
And the output (see below) proves one thing - the higher level out of the two is the one to determine the course of action, regardless of which entity was set to that level.
So my question is - why do we need more than one level to begin with?
DEBUG_DEBUG:
    debug
    info
    warning
    error
DEBUG_INFO:
    info
    warning
    error
DEBUG_WARNING:
    warning
    error
DEBUG_ERROR:
    error
INFO_DEBUG:
    info
    warning
    error
INFO_INFO:
    info
    warning
    error
INFO_WARNING:
    warning
    error
INFO_ERROR:
    error
WARNING_DEBUG:
    warning
    error
WARNING_INFO:
    warning
    error
WARNING_WARNING:
    warning
    error
WARNING_ERROR:
    error
ERROR_DEBUG:
    error
ERROR_INFO:
    error
ERROR_WARNING:
    error
ERROR_ERROR:
    error
You can attach more than one handler to a logger, and logger levels matter when propagating messages in a hierarchy.
Using multiple handlers lets you direct different severity levels to different outputs. You can put WARNING messages to the console, INFO and better go to the system log, and DEBUG messages go to an application-specific log file. This is what setting the log level on a handler is for, to configure what messages each handler will process, independently.
Logger levels matter in a hierarchy; all loggers are children of the root logger, and you can create further levels by using . dots in the name. You can then pick and choose what loggers produce what levels of output; setting a more strict log level means the logger will not process a message (including not handing it to the parent logger).
For example, many third-party libraries will produce log output at various levels. So, for a specific application, it could be quite helpful to see the ERROR level messages produced by the urllib3 library, but for SQLAlchemy you only want to see WARNING messages. You just configure the levels for these library loggers with a getLogger(name_of_library) call to stop them propagating lower level messages to the root logger, where the handlers typically live. 
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With