【问题标题】:What's the worst level log that I just logged?我刚刚记录的最差级别的日志是什么?
【发布时间】:2018-08-30 13:18:43
【问题描述】:

我已使用 logging 模块将日志添加到 Python 2 应用程序。 现在我想在最后添加一个结束语句,这取决于记录的最糟糕的事情。

如果记录的最糟糕的事情是 INFO 级别或更低,则打印“SUCCESS!”

如果记录的最糟糕的事情有警告级别,请写“成功!,带有警告。请检查日志”

如果记录的最糟糕的事情有 ERROR 级别,请写“FAILURE”。

有没有办法从记录器中获取这些信息?我缺少一些内置方法,例如 logging.getWorseLevelLogSoFar?

我目前的计划是将所有日志调用(logging.info 等)替换为对类中的包装函数的调用,该类也跟踪该信息。

我还考虑过以某种方式释放日志文件,读取并解析它,然后附加到它。这似乎比我目前的计划更糟糕。

还有其他选择吗?这似乎不是一个独特的问题。

我正在使用根记录器并希望继续使用它,但如果解决方案需要,可以更改为命名记录器。

【问题讨论】:

    标签: python logging


    【解决方案1】:

    正如您自己所说,我认为编写包装函数将是最简洁、最快的方法。问题是你需要一个全局变量,如果你不在一个类中工作

    global worst_log_lvl = logging.NOTSET
    
    def write_log(logger, lvl, msg):
        logger.log(lvl, msg)
        if lvl > worst_log_lvl:
            global worst_log_lvl
            worst_log_lvl = lvl
    

    或使worst_log_lvl 成为自定义类的成员,您可以在其中模拟logging.logger 的签名,您可以使用它来代替实际的记录器

     class CustomLoggerWrapper(object):
         def __init__(self):
             # setup of your custom logger
             self.worst_log_lvl = logging.NOTSET
    
         def debug(self):
             pass
    
         # repeat for other functions like info() etc.
    

    【讨论】:

      【解决方案2】:

      由于您只使用根记录器,您可以在其上附加一个过滤器来跟踪级别:

      import argparse
      import logging
      import random
      
      LEVELS = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
      
      class LevelTrackingFilter(logging.Filter):
          def __init__(self):
              self.level = logging.NOTSET
      
          def filter(self, record):
              self.level = max(self.level, record.levelno)
              return True
      
      def main():
          parser = argparse.ArgumentParser()
          parser.add_argument('maxlevel', metavar='MAXLEVEL', default='WARNING',
                              choices=LEVELS,
                              nargs='?', help='Set maximum level to log')
          options = parser.parse_args()
          maxlevel = getattr(logging, options.maxlevel)
      
          logger = logging.getLogger()
          logger.addHandler(logging.NullHandler())  # needs Python 2.7
          filt = LevelTrackingFilter()
          logger.addFilter(filt)
      
          for i in range(100):
              level = getattr(logging, random.choice(LEVELS))
              if level > maxlevel:
                  continue
              logger.log(level, 'message')
          if filt.level <= logging.INFO:
              print('SUCCESS!')
          elif filt.level == logging.WARNING:
              print('SUCCESS, with warnings. Please check the logs.')
          else:
              print('FAILURE')
      
      if __name__ == '__main__':
          main()
      

      【讨论】:

      • 对于任何路人:这个答案可能看起来太复杂了,但超过一半的复杂性是填充代码,以便拥有一个独立的示例。
      【解决方案3】:

      有一种“好”的方法可以通过使用上下文过滤器自动完成。

      TL;DR 我已经构建了一个包含以下上下文过滤器的包。您可以使用pip install ofunctions.logger_utils 安装它,然后使用它:

      from ofunctions import logger_utils
      
      logger = logger_utils.logger_get_logger(log_file='somepath', console=True)
      logger.error("Oh no!")
      logger.info("Anyway...")
      
      # Now get the worst called loglevel (result is equivalent to logging.ERROR level in this case)
      
      worst_level = logger_utils.get_worst_logger_level(logger)
      

      这是解释幕后发生的事情的长解决方案:

      让我们构建一个可以注入日志的 contextfilter 类:

      class ContextFilterWorstLevel(logging.Filter):
          """
          This class records the worst loglevel that was called by logger
          Allows to change default logging output or record events
          """
      
          def __init__(self):
              self._worst_level = logging.INFO
              if sys.version_info[0] < 3:
                  super(logging.Filter, self).__init__()
              else:
                  super().__init__()
      
          @property
          def worst_level(self):
              """
              Returns worst log level called
              """
              return self._worst_level
      
          @worst_level.setter
          def worst_level(self, value):
              # type: (int) -> None
              if isinstance(value, int):
                  self._worst_level = value
      
          def filter(self, record):
              # type: (str) -> bool
              """
              A filter can change the default log output
              This one simply records the worst log level called
              """
              # Examples
              # record.msg = f'{record.msg}'.encode('ascii', errors='backslashreplace')
              # When using this filter, something can be added to logging.Formatter like '%(something)s'
              # record.something = 'value'
              if record.levelno > self.worst_level:
                  self.worst_level = record.levelno
              return True
      

      现在将此过滤器注入您的记录器实例

      logger = logging.getLogger()
      logger.addFilter(ContextFilterWorstLevel())
      
      logger.warning("One does not simply inject a filter into logging")
      

      现在我们可以遍历当前的过滤器并像这样提取最差的称为 loglevel:

          for flt in logger.filters:
              if isinstance(flt, ContextFilterWorstLevel):
                  print(flt.worst_level)
      

      【讨论】:

        猜你喜欢
        • 2019-06-05
        • 1970-01-01
        • 1970-01-01
        • 2017-07-04
        • 2011-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多