【问题标题】:Logging setLevel is being ignored记录 setLevel 被忽略
【发布时间】:2017-08-23 21:01:58
【问题描述】:

以下代码是从文档中复制的。我应该能够看到所有的信息日志。但我没有。即使我将 setLevel 设置为 INFO,我也只能看到警告及以上。

为什么会这样? foo.py:

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

输出:

workingDirectory$ python foo.py
warn message
error message
critical message

信息和调试消息去哪儿了??

【问题讨论】:

    标签: python logging


    【解决方案1】:

    换行

    logger.setLevel(logging.DEBUG)
    

    logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    

    它应该按预期工作。如果您没有使用任何处理程序配置日志记录(如在您的帖子中 - 您只为您的记录器配置一个级别,但没有任何处理程序),您将获得一个内部处理程序"of last resort",它被设置为仅输出消息(没有其他格式)在WARNING 级别。

    【讨论】:

    • 等一下 - 这会设置全局/根日志记录设置,但如果我只想为 logger 设置级别怎么办?为什么logger.setLevel(logging.DEBUG) 不起作用?它应该。
    • @Greg 因为没有配置处理程序,所以日志记录使用内部“最后的处理程序”,其级别为WARNING:docs.python.org/3.2/library/logging.html#logging.lastResort
    • 不过,它应该可以工作。用户明确设置级别。
    • 所以在 getLogger() 之后调用logger.setlevel(logging.DEBUG) 是不够的。我还得惹上一个处理程序?这根本不直观。
    • 来自其他编程语言,这很愚蠢。
    【解决方案2】:

    尝试在那里运行logging.basicConfig()。值得注意的是,我看到你提到 INFO,但使用 DEBUG。如所写,它应该显示所有五条消息。将 DEBUG 替换为 INFO,您应该会看到四条消息。

    import logging
    
    logging.basicConfig()
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
    

    编辑:您是否已经在代码的其他地方设置了日志记录?使用提供的特定代码无法重现您注意到的确切行为。

    【讨论】:

    • 嗨@tabbek,感谢您的回复。我会尝试做 basicConfig() 代替。今晚晚些时候我可以做到。我没有任何其他代码。我有另一个带有处理程序的项目,它可以工作。然后我尝试开始一个新项目,没有工作。所以我写了这个准系统脚本。以上代码是foo.py的唯一内容。我在想有一些干扰正在发生什么的。我会及时通知你!
    • basicConfig() 进行一些基本设置和创建新的日志处理程序。来自 docs.python.org/2/library/logging.html 在 logging.basicConfig([**kwargs])
    • basicConfig() 应该附加一个带有警告级别的处理程序。那么这里的“logger.setLevel(logging.DEBUG)”是否同时设置了记录器和处理程序级别?
    • 为什么会这样?背后的原因是什么(需要无参数logging.basicConfig())?
    • 它使用默认值对日志系统进行一些基本设置。参考:docs.python.org/3/library/logging.html#logging.basicConfig“通过使用默认格式化程序创建一个 StreamHandler 并将其添加到根记录器来为日志系统进行基本配置。”
    【解决方案3】:

    正如一些用户所指出的,使用:

    logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    

    像在接受的答案中写的那样不是一个好的选项,因为它全局设置日志级别,因此它将记录来自每个记录器的调试消息。

    就我而言,仅为我的记录器设置日志级别的最佳解决方案是:

    import logging
    
    logger = logging.getLogger('MyLogger')
    handler = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.setLevel(logging.DEBUG)
    

    不是很直观的解决方案,但如果您只想为“MyLogger”而不是全局设置日志级别,则这是必要的。

    【讨论】:

    • 优雅的答案。谢谢!帮了我很多!
    • 非常感谢,我正在找这个!
    【解决方案4】:

    这在技术上也是一个“答案”,因为它可以“解决”问题。但我绝对不喜欢它。它不直观,我为此浪费了 2 个多小时。

    之前:

    import logging
    logger = logging.getLogger('foo')
    logger.setLevel(logging.INFO)
    logger.info('You can not see me')
    # Or you can just use the following one-liner in command line.
    # $ python -c "import logging; logger = logging.getLogger('foo'); logger.setLevel(logging.INFO); logger.info('You can not see me')"
    

    之后:

    import logging
    
    logging.debug('invisible magic')  # <-- magic
    
    logger = logging.getLogger('foo')
    logger.setLevel(logging.INFO)
    logger.info('But now you can see me')
    # Or you can just use the following one-liner in command line.
    $ python -c "import logging; logging.debug('invisible magic'); logger = logging.getLogger('foo'); logger.setLevel(logging.INFO); logger.info('But now you see me')"
    

    PS:将其与当前选择的答案以及@Vinay-Sajip 的解释进行比较,我可以理解为什么。但是,我仍然希望它不是那样工作。

    【讨论】:

    【解决方案5】:

    接受的答案在 Win10、Python 3.7.2 上对我不起作用。

    我的解决方案:

    logging.basicConfig(level=logging.DEBUG)
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    

    它是顺序敏感的。

    【讨论】:

    • 这个实际上完全没有setLevel,但也意味着它与上面更清晰的答案相同。
    【解决方案6】:

    您必须将根 logger 的 basicConfig 设置为 DEBUG,然后您可以将各个 logger 的级别设置为更严格的级别。

    这不是我所期望的。这是我必须做的:

    #!/usr/bin/env python3
    
    import logging
    # by default this is WARNING.  Leaving it as WARNING here overrides 
    # whatever setLevel-ing you do later so it seems they are ignored.
    logging.basicConfig(level=logging.DEBUG)
    
    l = logging.getLogger(__name__)
    l.setLevel(level=logging.INFO)
    # if I hadn't called basicConfig with DEBUG level earlier, 
    # info messages would STILL not be shown despite calling 
    # setLevel above.  However now debug messages will not be shown 
    # for l because setLevel set it to INFO
    
    l.warning('A warning message will be displayed')
    l.info('A friendly info message will be displayed')
    l.debug('A friendly debug message will not be displayed')
    

    【讨论】:

      【解决方案7】:

      简而言之,更改logging.basicConfig 中的level 会影响全局设置。 您最好为每个 logger 和 logger 中的特定 handler 设置 level

      以下是在控制台上显示所有级别并且仅在log_file.log 中记录messages >= errors 的示例。请注意,每个处理程序的 level 是不同的。

      import logging
      # Define logger
      logger = logging.getLogger('test')
      
      # Set level for logger
      logger.setLevel(logging.DEBUG)
      
      # Define the handler and formatter for console logging
      consoleHandler = logging.StreamHandler() # Define StreamHandler
      consoleHandler.setLevel(logging.DEBUG) # Set level
      concolsFormatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s') # Define formatter
      consoleHandler.setFormatter(concolsFormatter) # Set formatter
      logger.addHandler(consoleHandler) # Add handler to logger
      
      # Define the handler and formatter for file logging
      log_file = 'log_file'
      fileHandler = logging.FileHandler(f'{log_file}.log') # Define FileHandler
      fileHandler.setLevel(logging.ERROR) # Set level
      fileFormatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # Define formatter
      fileHandler.setFormatter(fileFormatter) # Set formatter
      logger.addHandler(fileHandler) # Add handler to logger
      
      # Test
      logger.debug('This is a debug')
      logger.info('This is an info')
      logger.warning('This is a warning')
      logger.error('This is an error')
      logger.critical('This is a critical')
      

      控制台输出:

      # Test
      test - DEBUG - This is a debug
      test - INFO - This is an info
      test - WARNING - This is a warning
      test - ERROR - This is an error
      test - CRITICAL - This is a critical
      

      文件log_file.log内容:

      2021-09-22 12:50:50,938 - test - ERROR - This is an error
      2021-09-22 12:50:50,938 - test - CRITICAL - This is a critical
      

      查看logger 的等级:

      logger.level
      

      结果应该是以下之一:

      10 # DEBUG
      20 # INFO
      30 # WARNING
      40 # ERROR
      50 # CRITICAL
      

      查看handlers 的等级:

      logger.handlers
      
      [<StreamHandler stderr (DEBUG)>,
       <FileHandler ***/log_file.log (ERROR)>]
      

      【讨论】:

      • 如果您想为每个模块设置一个记录器而不依赖于全局记录器,这是最好的答案。
      【解决方案8】:

      如果你想让它在没有 basicConfig 的情况下工作,你必须首先设置你将登录到记录器的最低级别。由于记录器设置了最小阈值,因此具有较低阈值但属于同一记录器的处理程序将不会收到那些较低阈值的消息,因为它们首先被记录器忽略。直观,但不明显。

      我们首先这样做:

      lgr = logging.getLogger(name)
      lgr.setLevel(logging.DEBUG)
      

      然后,使用您需要的不同级别设置处理程序,在我的情况下,我希望在 stdout 上进行 DEBUG 日志记录,并将 INFO 日志记录到旋转文件,因此我执行以下操作:

      rot_hndlr = RotatingFileHandler('filename.log',
                                      maxBytes=log_size,
                                      backupCount=3)
          
      rot_hndlr.setFormatter(formatter)
      rot_hndlr.setLevel(logging.INFO)
      lgr.addHandler(rot_hndlr)
      
      stream_hndlr = logging.StreamHandler()
      stream_hndlr.setFormatter(stream_formatter)
      lgr.addHandler(stream_hndlr)
      

      然后,为了测试,我这样做:

      lgr.debug("Hello")
      lgr.info("There")
      

      我的标准输出(控制台)将如下所示:

      Hello
      There
      

      我的filename.log 文件将如下所示:

      There
      

      【讨论】:

        【解决方案9】:

        我为这个问题找到的大多数答案都使用了根记录器的 basicConfig。

        这对于打算使用多个未使用 basicConfig 初始化的独立记录器的人没有帮助。 basicConfig 的使用意味着所有记录器的日志级别将被更改。它还具有生成重复日志的不幸副作用。

        所以我尝试了几天来尝试不同的方法来操作日志级别,并想出了一种最终奏效的方法。

        诀窍是不仅要更改所有处理程序的日志级别,还要更改记录器父级的所有处理程序。

            def setLevel(self, infoLevel):
            # To dynamically reset the loglevel, you need to also change the parent levels as well as all handlers!
        
            self.logger.parent.setLevel(infoLevel)
            for handler in self.logger.parent.handlers:
                handler.setLevel(infoLevel)
        
            self.logger.setLevel(infoLevel)
            for handler in self.logger.handlers:
                handler.setLevel(infoLevel)
        

        灵感来自于 basicConfig 更改了根记录器设置的事实,所以我尝试在不使用 basicConfig 的情况下做同样的事情。

        对于那些感兴趣的人,我在 Github 上做了一个小 Python 项目,说明了设置记录器的 loglevel 的不同问题(它部分工作),证明 SLogger(Sample Logger)实现工作,还说明了重复的日志使用多个未使用它初始化的记录器时,basicConfig 出现问题。

        https://github.com/FrancisChung/python-logging-playground

        TLDR:如果您只对记录器的工作示例代码感兴趣,下面列出了实现

        import logging
        
        CRITICAL = 50
        FATAL = CRITICAL
        ERROR = 40
        WARNING = 30
        WARN = WARNING
        INFO = 20
        DEBUG = 10
        NOTSET = 0
        
        
        class SLogger():
            """
            SLogger : Sample Logger class using the standard Python logging Library
        
            Parameters:
                name        : Name of the Logger
                infoLevel   : logging level of the Logger (e.g. logging.DEBUG/INFO/WARNING/ERROR)
            """
        
            def __init__(self, name: str, infoLevel=logging.INFO):
                try:
                    if name is None:
                        raise ValueError("Name argument not specified")
        
                    logformat = '%(asctime)s %(levelname)s [%(name)s %(funcName)s] %(message)s'
                    self.logformat = logformat
                    self.name = name.upper()
                    self.logger = logging.getLogger(self.name)
                    self.logger.setLevel(infoLevel)
        
                    self.add_consolehandler(infoLevel, logformat)
        
                except Exception as e:
                    if self.logger:
                        self.logger.error(str(e))
        
            def error(self, message):
                self.logger.error(message)
        
            def info(self, message):
                self.logger.info(message)
        
            def warning(self, message):
                self.logger.warning(message)
        
            def debug(self, message):
                self.logger.debug(message)
        
            def critical(self, message):
                self.logger.critical(message)
        
            def setLevel(self, infoLevel):
                # To dynamically reset the loglevel, you need to also change the parent levels as well as all handlers!
                self.logger.parent.setLevel(infoLevel)
                for handler in self.logger.parent.handlers:
                    handler.setLevel(infoLevel)
        
                self.logger.setLevel(infoLevel)
                for handler in self.logger.handlers:
                    handler.setLevel(infoLevel)
        
                return self.logger.level
        
            def add_consolehandler(self, infoLevel=logging.INFO,
                                   logformat='%(asctime)s %(levelname)s [%(name)s %(funcName)s] %(message)s'):
                sh = logging.StreamHandler()
                sh.setLevel(infoLevel)
        
                formatter = logging.Formatter(logformat)
                sh.setFormatter(formatter)
                self.logger.addHandler(sh)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-07
          • 2013-03-07
          • 1970-01-01
          • 1970-01-01
          • 2014-04-22
          • 2023-03-13
          • 2018-04-26
          • 2011-09-30
          相关资源
          最近更新 更多