How to add a custom loglevel to Python's logging facility

前端 未结 16 2243
旧时难觅i
旧时难觅i 2020-11-27 09:54

I\'d like to have loglevel TRACE (5) for my application, as I don\'t think that debug() is sufficient. Additionally log(5, msg) isn\'t what I want.

相关标签:
16条回答
  • 2020-11-27 10:21

    In case anyone wants an automated way to add a new logging level to the logging module (or a copy of it) dynamically, I have created this function, expanding @pfa's answer:

    def add_level(log_name,custom_log_module=None,log_num=None,
                    log_call=None,
                       lower_than=None, higher_than=None, same_as=None,
                  verbose=True):
        '''
        Function to dynamically add a new log level to a given custom logging module.
        <custom_log_module>: the logging module. If not provided, then a copy of
            <logging> module is used
        <log_name>: the logging level name
        <log_num>: the logging level num. If not provided, then function checks
            <lower_than>,<higher_than> and <same_as>, at the order mentioned.
            One of those three parameters must hold a string of an already existent
            logging level name.
        In case a level is overwritten and <verbose> is True, then a message in WARNING
            level of the custom logging module is established.
        '''
        if custom_log_module is None:
            import imp
            custom_log_module = imp.load_module('custom_log_module',
                                                *imp.find_module('logging'))
        log_name = log_name.upper()
        def cust_log(par, message, *args, **kws):
            # Yes, logger takes its '*args' as 'args'.
            if par.isEnabledFor(log_num):
                par._log(log_num, message, args, **kws)
        available_level_nums = [key for key in custom_log_module._levelNames
                                if isinstance(key,int)]
    
        available_levels = {key:custom_log_module._levelNames[key]
                                 for key in custom_log_module._levelNames
                                if isinstance(key,str)}
        if log_num is None:
            try:
                if lower_than is not None:
                    log_num = available_levels[lower_than]-1
                elif higher_than is not None:
                    log_num = available_levels[higher_than]+1
                elif same_as is not None:
                    log_num = available_levels[higher_than]
                else:
                    raise Exception('Infomation about the '+
                                    'log_num should be provided')
            except KeyError:
                raise Exception('Non existent logging level name')
        if log_num in available_level_nums and verbose:
            custom_log_module.warn('Changing ' +
                                      custom_log_module._levelNames[log_num] +
                                      ' to '+log_name)
        custom_log_module.addLevelName(log_num, log_name)
    
        if log_call is None:
            log_call = log_name.lower()
    
        setattr(custom_log_module.Logger, log_call, cust_log)
        return custom_log_module
    
    0 讨论(0)
  • 2020-11-27 10:25

    Addition to Mad Physicists example to get file name and line number correct:

    def logToRoot(message, *args, **kwargs):
        if logging.root.isEnabledFor(levelNum):
            logging.root._log(levelNum, message, args, **kwargs)
    
    0 讨论(0)
  • 2020-11-27 10:26

    I think you'll have to subclass the Logger class and add a method called trace which basically calls Logger.log with a level lower than DEBUG. I haven't tried this but this is what the docs indicate.

    0 讨论(0)
  • 2020-11-27 10:27

    based on pinned answer, i wrote a little method which automaticaly create new logging levels

    def set_custom_logging_levels(config={}):
        """
            Assign custom levels for logging
                config: is a dict, like
                {
                    'EVENT_NAME': EVENT_LEVEL_NUM,
                }
            EVENT_LEVEL_NUM can't be like already has logging module
            logging.DEBUG       = 10
            logging.INFO        = 20
            logging.WARNING     = 30
            logging.ERROR       = 40
            logging.CRITICAL    = 50
        """
        assert isinstance(config, dict), "Configuration must be a dict"
    
        def get_level_func(level_name, level_num):
            def _blank(self, message, *args, **kws):
                if self.isEnabledFor(level_num):
                    # Yes, logger takes its '*args' as 'args'.
                    self._log(level_num, message, args, **kws) 
            _blank.__name__ = level_name.lower()
            return _blank
    
        for level_name, level_num in config.items():
            logging.addLevelName(level_num, level_name.upper())
            setattr(logging.Logger, level_name.lower(), get_level_func(level_name, level_num))
    
    

    config may smth like that:

    new_log_levels = {
        # level_num is in logging.INFO section, that's why it 21, 22, etc..
        "FOO":      21,
        "BAR":      22,
    }
    
    0 讨论(0)
提交回复
热议问题