【问题标题】:Logging format with multiple timestamps?具有多个时间戳的日志记录格式?
【发布时间】:2023-03-25 14:39:01
【问题描述】:

由于我的应用程序的特定要求,我需要将相同的时间戳添加到单个日志条目中,但每个时间戳的格式不同。这两个时间戳彼此不相邻,因此其他日志记录字段位于它们之间。由于datefmtformatter 中作为第二个参数传递给format,所以我还没有弄清楚如何为单个日志记录条目提供两种不同的日期格式。

我要问的唯一问题是在没有自定义格式化程序或过滤器的情况下使用 Python 是否可行。

【问题讨论】:

  • 不能同时拥有两种格式吗?类似于'%Y-%m-%d %H:%M:%S - %a %b %d %Y %H:%M:%S'
  • 再次,您所做的假设是两个时间戳彼此相邻。如果它们需要出现在不同的位置,并且它们之间有其他日志字段,这是如何工作的?
  • @robross0606 您能否编辑您的问题以包含您想要的确切日期格式? (我可能有适合你的答案,但有一些注意事项)
  • @wim 我真的对只适用于非常特定格式的答案不感兴趣。每种格式的要求可能会发生变化,但障碍保持不变。据我所知,Python 的内置日志记录语法无法让您将多个时间戳添加到具有不同格式的单个日志记录条目中。
  • 1.为什么您的日志记录日期格式的要求会发生变化? 2. 您对使用logging.Formatter 子类的答案感兴趣吗? 3. 您对使用第 3 方日志记录模块的答案感兴趣吗? (stlib 日志记录灵活但有限)

标签: python date logging formatting


【解决方案1】:

由于源代码的编写方式,这在 stdlib logging 中是不可能的。格式化程序类将使用formatTime 方法在每个LogRecord 实例上设置一个asctime。正如您在日志记录模块中看到的,代码here,相关字段%(asctime) 实际上是一个字符串属性而不是datetime 实例。这意味着除了填充和对齐等琐碎的字符串操作之外,它不能接受自定义格式。

我同意你的观点,这是 stdlib logging 模块中的一个缺点,并敦促你考虑一个更强大的日志框架,例如 structlog

您的问题的其他读者可能不会那么反对在 stdlib 日志记录中使用 Formatter 子类。对于他们,我建议定义一个格式化程序,它在 LogRecord 实例上使用 datetime 实例,并结合大括号样式的格式化程序模板字符串。这允许不受限制地使用具有多种 strftime 格式的 asctime 字段。

设置比使用FilterLoggingAdapter 更简单,它只是formatTime 方法的单行覆盖:

import logging
from datetime import datetime

class MyFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        return datetime.fromtimestamp(record.created)

fmt = '{asctime:%Y-%m-%d %H:%M:%S} - {levelname} - {asctime:%a %b %d %Y} - {message}'
logger = logging.getLogger()
handler = logging.StreamHandler()
handler.setFormatter(MyFormatter(fmt=fmt, style='{'))
logger.addHandler(handler)
logger.warning('the message')

注意:如果您需要的格式之一只是时间戳(自纪元以来的秒数),那么您根本不需要自定义格式化程序“体操” - 您只需使用 @987654338 @ 字段(接受浮点格式自定义)与asctime 字段结合使用。

【讨论】:

    【解决方案2】:

    带有原始时间戳和人类可读日期的非常基本的示例:

    import logging
    import sys
    from datetime import datetime
    
    
    class ContextFilter(logging.Filter):
        """
        This is a filter which injects contextual information into the log.
        """
    
        @staticmethod
        def some_format(timestamp):
            return datetime.fromtimestamp(
                timestamp
            ).strftime('%Y-%m-%d %H:%M:%S')
    
        def filter(self, record):
    
            record.format1 = record.created
            record.format2 = self.some_format(record.created)
            return True
    
    
    formatter = logging.Formatter('%(format1)s - %(format2)s - %(name)s - %(levelname)s: %(message)s')
    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(formatter)
    handler.setLevel(logging.DEBUG)
    logger = logging.Logger('test')
    logger.addHandler(handler)
    
    f = ContextFilter()
    logger.addFilter(f)
    
    logger.info('test')
    

    输出: 1525883052.4808118 - 2018-05-09 19:24:12 - test - INFO: test

    【讨论】:

    • 我会说这可能不符合避免“自定义格式化程序体操”的条件。
    • @robross0606 您能否修改原始问题,为您所说的“自定义格式化程序体操”提供技术指南——因为在我的阅读中,这种方法非常简洁、简单,使用可扩展性挂钩标准logging 模块打算让您使用,并且在任何意义上看起来都不像“体操”。
    • +1 我同意这个解决方案很好 - 它类似于文档中给出的配方:Using Filters to impart contextual information。这里的downvote是没有根据的!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多