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

前端 未结 16 2242
旧时难觅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:03

    As alternative to adding an extra method to the Logger class I would recommend using the Logger.log(level, msg) method.

    import logging
    
    TRACE = 5
    logging.addLevelName(TRACE, 'TRACE')
    FORMAT = '%(levelname)s:%(name)s:%(lineno)d:%(message)s'
    
    
    logging.basicConfig(format=FORMAT)
    l = logging.getLogger()
    l.setLevel(TRACE)
    l.log(TRACE, 'trace message')
    l.setLevel(logging.DEBUG)
    l.log(TRACE, 'disabled trace message')
    
    0 讨论(0)
  • 2020-11-27 10:04

    While we have already plenty of correct answers, the following is in my opinion more pythonic:

    import logging
    
    from functools import partial, partialmethod
    
    logging.TRACE = 5
    logging.addLevelName(logging.TRACE, 'TRACE')
    logging.Logger.trace = partialmethod(logging.Logger.log, logging.TRACE)
    logging.trace = partial(logging.log, logging.TRACE)
    

    If you want to use mypy on your code, it is recommended to add # type: ignore to suppress warnings from adding attribute.

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

    Tips for creating a custom logger:

    1. Do not use _log, use log (you don't have to check isEnabledFor)
    2. the logging module should be the one creating instance of the custom logger since it does some magic in getLogger, so you will need to set the class via setLoggerClass
    3. You do not need to define __init__ for the logger, class if you are not storing anything
    # Lower than debug which is 10
    TRACE = 5
    class MyLogger(logging.Logger):
        def trace(self, msg, *args, **kwargs):
            self.log(TRACE, msg, *args, **kwargs)
    

    When calling this logger use setLoggerClass(MyLogger) to make this the default logger from getLogger

    logging.setLoggerClass(MyLogger)
    log = logging.getLogger(__name__)
    # ...
    log.trace("something specific")
    

    You will need to setFormatter, setHandler, and setLevel(TRACE) on the handler and on the log itself to actually se this low level trace

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

    I'm confused; with python 3.5, at least, it just works:

    import logging
    
    
    TRACE = 5
    """more detail than debug"""
    
    logging.basicConfig()
    logging.addLevelName(TRACE,"TRACE")
    logger = logging.getLogger('')
    logger.debug("n")
    logger.setLevel(logging.DEBUG)
    logger.debug("y1")
    logger.log(TRACE,"n")
    logger.setLevel(TRACE)
    logger.log(TRACE,"y2")
        
    

    output:

    DEBUG:root:y1

    TRACE:root:y2

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

    I took the avoid seeing "lambda" answer and had to modify where the log_at_my_log_level was being added. I too saw the problem that Paul did – I don't think this works. Don't you need logger as the first arg in log_at_my_log_level? This worked for me

    import logging
    DEBUG_LEVELV_NUM = 9 
    logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
    def debugv(self, message, *args, **kws):
        # Yes, logger takes its '*args' as 'args'.
        self._log(DEBUG_LEVELV_NUM, message, args, **kws) 
    logging.Logger.debugv = debugv
    
    0 讨论(0)
  • 2020-11-27 10:13

    Combining all of the existing answers with a bunch of usage experience, I think that I have come up with a list of all the things that need to be done to ensure completely seamless usage of the new level. The steps below assume that you are adding a new level TRACE with value logging.DEBUG - 5 == 5:

    1. logging.addLevelName(logging.DEBUG - 5, 'TRACE') needs to be invoked to get the new level registered internally so that it can be referenced by name.
    2. The new level needs to be added as an attribute to logging itself for consistency: logging.TRACE = logging.DEBUG - 5.
    3. A method called trace needs to be added to the logging module. It should behave just like debug, info, etc.
    4. A method called trace needs to be added to the currently configured logger class. Since this is not 100% guaranteed to be logging.Logger, use logging.getLoggerClass() instead.

    All the steps are illustrated in the method below:

    def addLoggingLevel(levelName, levelNum, methodName=None):
        """
        Comprehensively adds a new logging level to the `logging` module and the
        currently configured logging class.
    
        `levelName` becomes an attribute of the `logging` module with the value
        `levelNum`. `methodName` becomes a convenience method for both `logging`
        itself and the class returned by `logging.getLoggerClass()` (usually just
        `logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
        used.
    
        To avoid accidental clobberings of existing attributes, this method will
        raise an `AttributeError` if the level name is already an attribute of the
        `logging` module or if the method name is already present 
    
        Example
        -------
        >>> addLoggingLevel('TRACE', logging.DEBUG - 5)
        >>> logging.getLogger(__name__).setLevel("TRACE")
        >>> logging.getLogger(__name__).trace('that worked')
        >>> logging.trace('so did this')
        >>> logging.TRACE
        5
    
        """
        if not methodName:
            methodName = levelName.lower()
    
        if hasattr(logging, levelName):
           raise AttributeError('{} already defined in logging module'.format(levelName))
        if hasattr(logging, methodName):
           raise AttributeError('{} already defined in logging module'.format(methodName))
        if hasattr(logging.getLoggerClass(), methodName):
           raise AttributeError('{} already defined in logger class'.format(methodName))
    
        # This method was inspired by the answers to Stack Overflow post
        # http://stackoverflow.com/q/2183233/2988730, especially
        # http://stackoverflow.com/a/13638084/2988730
        def logForLevel(self, message, *args, **kwargs):
            if self.isEnabledFor(levelNum):
                self._log(levelNum, message, args, **kwargs)
        def logToRoot(message, *args, **kwargs):
            logging.log(levelNum, message, *args, **kwargs)
    
        logging.addLevelName(levelNum, levelName)
        setattr(logging, levelName, levelNum)
        setattr(logging.getLoggerClass(), methodName, logForLevel)
        setattr(logging, methodName, logToRoot)
    
    0 讨论(0)
提交回复
热议问题