Home > Net >  python3 - custom logger not working without basicConfig method?
python3 - custom logger not working without basicConfig method?

Time:03-06

I've got this code:

    def __init__(self, log_level: str = None):
        self.log_level = log_level if log_level and log_level.lower() in ['debug', 'warning', 'info',
                                                                          'error'] else 'error'
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(getattr(logging, self.log_level.upper()))

And i want to create simple logger without configurations. This code is not working without this line: logging.basicConfig()

Why? I can't really understand. So if you try something like this:

self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.DEBUG)
self.logger.debug('test')

it would not print anything. But this will:

logging.basicConfig()
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.DEBUG)
self.logger.debug('test')

or this:

self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.DEBUG)
logging.debug("don't care")
self.logger.debug('test')

CodePudding user response:

There are a couple reasons that lead to this behavior. The important part to understand is that Python has a hierarchical logging system. Your logger is a child of the 'root' logger. Each logger can be configured independently. Log messages propagate from children up to the root logger (unless logger.propagate is set to False).

logging.basicConfig will setup the configuration for the root logger including a handler, which is necessary for messages to reach stderr (or whatever other destination), provided that the root logger has not already been configured with a handler.

>>> import logging
>>> logger = logging.getLogger(__name__)
>>> logger.setLevel(logging.DEBUG)
>>> root_logger = logger.root
>>> root_logger.handlers  # by default, there are no handlers
[]
>>> logger.debug('this wont show')
>>> logging.basicConfig()  # sets handler on the root logger
>>> root_logger.handlers
[<StreamHandler <stderr> (NOTSET)>]
>>> logger.debug('test')  # now this will go to stderr
DEBUG:__main__:test

We can also affirm that this is the result of the root logger configuration by setting logger.propagate = False and then messages will no longer reach stderr.

>>> logger.propagate = False
>>> logger.debug('this wont show')

We can also see our logger has no handlers of its own, but we can add them, if we want our logger to output messages independent of the root logger (to which we no longer propagate messages).

>>> logger.handlers
[]
>>> logger.addHandler(logging.StreamHandler())
>>> logger.debug('test')
test

We can re-enable propagation and see both loggers will output the message (with each format of each logger)

>>> logger.propagate = True
>>> logger.debug('test')
test
DEBUG:__main__:test

CodePudding user response:

Logger must have defined handler. If you create logger using logging.getLogger(__name__) you must addHandler. Adding handler is part of basicConfig configuration.

import logging

logger = logging.getLogger('test')
logger.addHandler(logging.StreamHandler())  # if you comment this line, logger will not print out message
logger.setLevel(logging.DEBUG)
logger.info('hi')

You can add multiple handles to one logger. This example will print 2 messages (becasue there are two StreamHandlers).

import logging

logger = logging.getLogger('test')
logger.addHandler(logging.StreamHandler()) 
logger.addHandler(logging.StreamHandler()) 
logger.setLevel(logging.DEBUG)
logger.info('hi')

Each hadnler can have different formater, different logging_level, different output. This is usefull if you want to print in console logs from all levels, but to file only wirte critical and error level messages.

  • Related