【问题标题】:How to insert newline in python logging?如何在 python 日志中插入换行符?
【发布时间】:2013-12-05 09:06:14
【问题描述】:
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', datefmt='%H:%M:%S')
logging.info('hello')
logging.warning('\n new hello')

11:15:01 信息你好
11:16:49 警告
新的你好

由于日志很拥挤,我想在asctimelevelname 之前显式插入换行符。不修改format可以吗?

我查看了logging 模块并搜索了一下,找不到可行的方法。

【问题讨论】:

    标签: python logging newline


    【解决方案1】:

    我有两个解决方案,第一个很简单,但是输出不是很干净。第二种方法将产生您想要的确切输出,但涉及更多。

    方法一

    要产生一个空行,只需用一个新行记录一个空字符串:

    import logging
    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', datefmt='%H:%M:%S')
    logging.info('hello')
    logging.info('\n')
    logging.warning('new hello')
    

    输出会有一个空的信息行,不是很干净:

    16:07:26 信息你好
    16:07:26 信息

    16:07:26 警告新的你好

    方法二

    在这个方法中,我创建了两个不同的处理程序。我大部分时间都在使用console_handler。当我需要换行时,我会切换到第二个处理程序 blank_handler

    import logging
    import types
    
    def log_newline(self, how_many_lines=1):
        # Switch handler, output a blank line
        self.removeHandler(self.console_handler)
        self.addHandler(self.blank_handler)
        for i in range(how_many_lines):
            self.info('')
    
        # Switch back
        self.removeHandler(self.blank_handler)
        self.addHandler(self.console_handler)
    
    def create_logger():
        # Create a handler
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        console_handler.setFormatter(logging.Formatter(fmt="%(name)s %(levelname)-8s: %(message)s"))
    
        # Create a "blank line" handler
        blank_handler = logging.StreamHandler()
        blank_handler.setLevel(logging.DEBUG)
        blank_handler.setFormatter(logging.Formatter(fmt=''))
    
        # Create a logger, with the previously-defined handler
        logger = logging.getLogger('logging_test')
        logger.setLevel(logging.DEBUG)
        logger.addHandler(console_handler)
    
        # Save some data and add a method to logger object
        logger.console_handler = console_handler
        logger.blank_handler = blank_handler
        logger.newline = types.MethodType(log_newline, logger)
    
        return logger
    
    if __name__ == '__main__':
        logger = create_logger()
        logger.info('Start reading database')
        logger.info('Updating records ...')
        logger.newline()
        logger.info('Finish updating records')
    

    输出就是你想看到的:

    logging_test INFO    : Start reading database
    logging_test INFO    : Updating records ...
    
    logging_test INFO    : Finish updating records
    

    讨论

    • 如果您能忍受不太完美的输出,方法 1 是可行的方法。它的优点是简单、省力。
    • 第二种方法正确地完成了这项工作,但它有点复杂。它创建两个不同的处理程序并切换它们以实现您的目标。
    • 使用方法 2 的另一个缺点是您必须通过搜索 logging 并将它们替换为 logger 来更改您的代码。您必须注意仅替换相关部分,并保留logging.DEBUG 等文本。

    【讨论】:

    • 方法 1 的另一个缺点是,如果不同的工作人员在两者之间记录一些内容,则很难跟踪。
    【解决方案2】:

    你不能在第一个 hello 之后添加换行符吗?即

    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', datefmt='%H:%M:%S')
    logging.info('hello\n')
    logging.info('new hello')
    

    哪个会输出

    2014-08-06 11:37:24,061 INFO    : hello
    
    2014-08-06 11:37:24,061 INFO    : new hello
    

    【讨论】:

    • 我一开始也认为这也是显而易见的解决方案,但后来空白行将取决于日志记录级别。
    【解决方案3】:

    使用自定义Formatter,它在不同的时间使用不同的格式字符串。您无法使用 basicConfig() 执行此操作 - 您必须使用 logging API 的其他部分。

    class MyFormatter(logging.Formatter):
        def format(self, record):
            # set self._fmt to value with or without newline,
            # as per your decision criteria
            # self._fmt = ...
            return super(MyFormatter, self).format(record)
    

    或者,您可以调用super 方法,然后修改字符串以在返回之前插入换行符(如果它取决于行长,比如说)。

    【讨论】:

    • 您可以采用这种方法,但如果您想在实际消息中添加新行,请编辑 record.msg
    【解决方案4】:

    我想出的插入换行符的最简单方法:

    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s\n\r%(message)s', datefmt='%H:%M:%S')
    logging.info('hello')
    logging.info('new hello')
    

    11:50:32 信息
    你好
    11:50:32 信息
    新的你好

    【讨论】:

    • 这将在 python 3.6.1 中逐字显示\n\r
    【解决方案5】:

    如果您只是想在开发中输出一些调试代码,那么您可能不想花时间在这上面。 5 秒修复是这样的;

    str = "\n\n\n"
    log.getLogger().debug(str)
    

    记录器是标准的python记录器

    【讨论】:

      【解决方案6】:

      类似的东西。在asctimelevelname 之间添加\nlogging.basicConfig

      >>> logging.basicConfig(level=logging.DEBUG, format='%(asctime)s\n %(levelname)s %(message)s',datefmt='%H:%M:%S') 
      

      【讨论】:

      • 我只在特殊场合需要换行符。大多数时候原始格式都很好。
      • 这很简单。我在开头和结尾添加

        标签以在浏览器中远程检查文件。不是最好的,但很整洁

      【解决方案7】:

      作为 Hai Vu 的 方法 2 的替代方案,您还可以在每次要记录新行时重置处理程序的 Formatter

      import logging
      import types
      
      def log_newline(self, how_many_lines=1):
          # Switch formatter, output a blank line
          self.handler.setFormatter(self.blank_formatter)
      
          for i in range(how_many_lines):
              self.info('')
      
          # Switch back
          self.handler.setFormatter(self.formatter)
      
      
      def create_logger():
          # Create a handler
          handler = logging.StreamHandler()
          handler.setLevel(logging.DEBUG)
          formatter = logging.Formatter(fmt="%(name)s %(levelname)-8s: %(message)s")        
          blank_formatter = logging.Formatter(fmt="")
          handler.setFormatter(formatter)
      
      
          # Create a logger, with the previously-defined handler
          logger = logging.getLogger('logging_test')
          logger.setLevel(logging.DEBUG)
          logger.addHandler(handler)
      
          # Save some data and add a method to logger object
          logger.handler = handler
          logger.formatter = formatter
          logger.blank_formatter = blank_formatter
          logger.newline = types.MethodType(log_newline, logger)
      
          return logger
      
      if __name__ == '__main__':
          logger = create_logger()
          logger.info('Start reading database')
          logger.info('Updating records ...')
          logger.newline()
          logger.info('Finish updating records')
      

      输出

      logging_test INFO    : Start reading database  
      logging_test INFO    : Updating records ...
      
      logging_test INFO    : Finish updating records
      

      这样做的好处是您只有一个处理程序。例如,如果您想在每次新运行程序时清理日志文件,您可以定义一个 FileHandlermode-attribute 来写入。

      【讨论】:

      • 我很好奇:为什么这个解决方案不是线程安全的,而原来的不是?提前致谢。
      • 我会回答自己。这不是线程安全的,因为如果一个线程将写入一个新行,并且如果现在另一个线程将在前一个线程重置格式化程序之前记录信息,则后者将使用一个空的格式化程序进行记录,这是我们不想要的.
      【解决方案8】:

      如果没有日志服务,写入日志文件会怎样?

      fn_log = 'test.log'
      logging.basicConfig(filename=fn_log, level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s', datefmt='%H:%M:%S')
      logging.info('hello')
      logging.warning('no empty line')
      
      def empty_line(fn_log):
          new_empty_line = open(fn_log,'a+')
          new_empty_line.write('\n')
          new_empty_line.close()
      
      empty_line(fn_log)
      logging.warning('hello') 
      

      输出:

      09:26:00 INFO hello
      11:51:05 INFO hello
      11:51:05 WARNING no empty line
      
      11:51:05 WARNING hello
      

      【讨论】:

        【解决方案9】:

        按照 Vinay Salip 的有用答案(如下),我这样做了(我使用的是 python3 超类约定,但 super(MyFormatter, self) 也可以工作)...

        class MyFormatter(logging.Formatter):
            def format(self, record):
                return super().format(record).replace(r'\n', '\n')
        

        然后,我可以按如下方式嵌入换行符:

        logging.info('Message\\n\\n\\n\\nOther stuff')
        

        logging.info(r'Message\n\n\n\nOther stuff')
        

        【讨论】:

          【解决方案10】:

          如果您使用 FileHandler 或其后代,这两个函数可能会有所帮助。另一个好处是附加到记录器的所有 FileHandler 类型处理程序都应该获得换行符。

          def getAllLoggerFilenames(logger):
            """ Returns array of all log filenames attached to the logger. """
            logFiles = [];
            parent = logger.__dict__['parent'];
            if parent.__class__.__name__ == 'RootLogger':
              for h in logger.__dict__['handlers']:
                if h.baseFilename:
                  logFiles.append(h.baseFilename);
            else:
              logFiles = getAllLoggerFilenames(parent);
            return logFiles;
          
          def logBlankLine(logger):
            """ This utility method writes a blank line to the log. """
            logNames = getAllLoggerFilenames(logger)
            for fn in logNames:
              with open(fn, 'a') as fh:
                fh.write("\n")
          

          用法:

          # We use YAML for logging config files, YMMV:
          with open(logConfig, 'rt') as f:
            logging.config.dictConfig(yaml.safe_load(f.read()))
          
          logger = logging.getLogger("test.test")
          
          logger.info("line 1")
          logBlankLine(logger)
          logger.info("line 2")
          

          输出:

          2019/12/22 16:33:59.152: INFO    : test.test                 : line 1
          
          2019/12/22 16:33:59.152: INFO    : test.test                 : line 2
          

          【讨论】:

          • 不能保证打开文件的第二个句柄以写入空白,如您的示例所示。大多数操作系统不保证两个句柄的日志写入会被序列化。
          【解决方案11】:

          如果您使用的是 Python 3,最简单的解决方案是使用 f 字符串:

          logging.info( f'hello\n' )
          

          【讨论】:

          • Python 3.6+,特别是。
          • 在你的代码中使用 f-strings 是没有意义的......在这里使用普通字符串没有区别......
          【解决方案12】:

          您可以尝试以下解决方案。它简单明了。

          logging.debug("\b" * 20)  # output blank line using escape character
          logging.debug("debug message")
          

          【讨论】:

          • lmao 这是什么黑客
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-22
          • 1970-01-01
          • 1970-01-01
          • 2018-05-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多