Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python django logging: One single logger with more than one logging level

tl;dr: Is there any way to have one single logger that manages all loglevels?

To log in my project, I am currntly running:

logger = logging.getLogger('info.contract')
logger.info("Some info message...")

Which prints 2021-01-24 03:05:02,063 [contract_generator] Some info message... to console.

The info.contract logger is defined in my settings.py:

    'loggers': {
        'info': {
            'handlers' : ['info_logfile'],
            'level': 'INFO',
        },
        'info.contract': {
            'handlers': ['contract_info_logfile'],
            'level': 'INFO',
            'propagate': True,
        }
    },
    'handlers': {
        'info_logfile': {
            'class': 'logging.FileHandler',
            'filename': 'info.log',
            'formatter': 'default',
        },
        'contract_info_logfile': {
            'class': 'logging.FileHandler',
            'filename': 'contract.info.log',
            'formatter': 'default',
        }
    }
    'formatters': {
        'default': {
            'format': '%(asctime)s [%(module)s] %(message)s',
        }
    }
}

The problem I am having is the following:

In order to log to different files depending on the loglevel, I would have to create a new logger for each loglevel. Which is to say, the following doesn't work:

logger = logging.getLogger('info.contract')
logger.info("Some info message...")

## this part is new
logger.debug("Some debug message...")

Since logger uses the info.contract logger defined in settings.py, which has its 'level' setting set to 'INFO' (as shown above). Interestingly though, I don't get any errors. The logger simply doesn't log the entries when I use the .debug() function.

So in order to log to different files depending on the loglevel, I would indeed have to create a new logger for each loglevel, even though that produces a lot of duplicate code:

##---snip---##
'loggers': {
        'info': {
            'handlers' : ['std_err', 'info_logfile'],
            'level': 'INFO',
        },
        'info.contract': {
            'handlers': ['contract_info_logfile'],
            'level': 'INFO',
            'propagate': True,
        }
        'debug': {                                     ## duplicate code
            'handlers' : ['std_err', 'debug_logfile'],
            'level': 'DEBUG',
        },
            'debug.contract': {                        ## duplicate code
            'handlers': ['contract_debug_logfile'],
            'level': 'DEBUG',
            'propagate': True,
        }
},
##---snip---##

So then in my code I would have to do:

info_logger = logging.getLogger('info.contract')
debug_logger = logging.getLogger('debug.contract')

info_logger.info("Some info message...")
debug_logger.debug("Some debug message...")

Which implies that it is necessary to instantiate a new logger object for every loglevel I need to use. This seems cumbersome and is maybe inefficient.

My question is:

Is there any way to have one single logger that manages all loglevels? In other words, is it possible to do the following:

some_multilevel_logger = logging.getLogger('some_multilevel_logger')
some_multilevel_logger.info("Some info message...")
some_multilevel_logger.debug("Some debug message...")

And that would produce the same results as the previous snippet, in which 2 loggers are defined.

If that behavior is not possible, is it considered bad practice to create one logger for every loglevel? What about massive projects with many loglevels?

Thank you!

like image 289
Fabián Montero Avatar asked Nov 05 '25 13:11

Fabián Montero


1 Answers

Best solution I found was to create multiple handlers for one single logger:

LOGGING = {
    'version': 1,
    'disable_existing_loggers' : False,
    'loggers': {
        'general': {
            'handlers': ['error', 'info', 'debug'],
            'level': 1
        }
    },
    'handlers': {
        'std_err': {
            'class': 'logging.StreamHandler'
        },
        'info': {
            'class': 'logging.FileHandler',
            'filename': 'info.log',
            'level': 'INFO',
            'formatter': 'default',
        },
        'error': {
            'class': 'logging.FileHandler',
            'filename': 'error.log',
            'level': 'ERROR',
            'formatter': 'error',
        },
        'debug': {
            'class': 'logging.FileHandler',
            'filename': 'debug.log',
            'level': 'DEBUG',
            'formatter': 'default',
        },
    },
    'formatters': {
        'default': {
            'format': '%(asctime)s [%(module)s | %(levelname)s] %(message)s',
        },
        'error': {
            'format': '%(asctime)s [%(module)s | %(levelname)s] %(message)s @ %(pathname)s : %(lineno)d : %(funcName)s',
        },
    },
}

Which allows the following:

logger = logging.getLogger('general')
logger.error("Some error message...")
logger.debug("Debug message...")
logger.info("Some info message..")

it even allows:

logger.exception("Exception message", fmt=std.FAIL)
like image 70
Fabián Montero Avatar answered Nov 08 '25 09:11

Fabián Montero