【问题标题】:Using LogRecordFactory in python to add custom fields for logging在 python 中使用 LogRecordFactory 添加自定义字段进行日志记录
【发布时间】:2020-01-03 22:00:07
【问题描述】:

我正在尝试使用 LogRecordFactory 在我的日志记录中添加一个自定义字段。我反复调用一个类,每次我这样做时,我都想在 init 模块中设置 custom_attribute ,以便类中的其余代码将具有此属性。但我无法让它发挥作用。我发现以下内容有效,但它是静态的。

import logging

old_factory = logging.getLogRecordFactory()

def record_factory(*args, **kwargs):
    record = old_factory(*args, **kwargs)
    record.custom_attribute = "whatever"
    return record



logging.basicConfig(format="%(custom_attribute)s - %(message)s")
logging.setLogRecordFactory(record_factory)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logging.debug("test")

这将正确输出:

whatever - test

但是,我的用例是 custom_attribute 会有所不同。每次我调用一个特定的函数时,我都想改变它。因此,似乎 record_factory 需要传递给它的另一个参数,以便它可以使用新参数返回正确的记录。但我想不通。我尝试向函数添加参数,但是当我调用时,我得到:

TypeError: __init__() missing 7 required positional arguments: 'name', 'level', 'pathname', 'lineno', 'msg', 'args', and 'exc_info'

我认为这与 *args 和 **kwargs 有关,但我真的不知道。另外,为什么在logging.setLogRecordFactory调用record_factory之后没有括号?我从未见过这样的函数。

【问题讨论】:

    标签: python-3.x logging


    【解决方案1】:

    你可以尝试使用closure:

    import logging
    
    old_factory = logging.getLogRecordFactory()
    
    def record_factory_factory(context_id):
        def record_factory(*args, **kwargs):
            record = old_factory(*args, **kwargs)
            record.custom_attribute = context_id
            return record
        return record_factory
    
    
    logging.basicConfig(format="%(custom_attribute)s - %(message)s")
    logging.setLogRecordFactory(record_factory_factory("whatever"))
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    logging.debug("test")
    
    logging.setLogRecordFactory(record_factory_factory("whatever2"))
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    logging.debug("test")
    

    结果:

    $ python3 log_test.py                                                          
    whatever - test
    whatever2 - test
    

    【讨论】:

      【解决方案2】:

      我在尝试做类似的事情时偶然发现了这个问题。这就是我解决它的方法,假设你想在每个日志行中添加一个叫做 xyz 的东西(下面有进一步的解释):

      import logging
      import threading
       
      
      thread_local = threading.local()
       
      
      def add_xyz_to_logrecords(xyz):
          factory = logging.getLogRecordFactory()
          if isinstance(factory, XYZLogFactory):
              factory.set_xyz(xyz)
          else:
              logging.setLogRecordFactory(XYZLogFactory(factory, xyz))
       
      
      class XYZLogFactory():
          def __init__(self, original_factory, xyz):
              self.original_factory = original_factory
              thread_local.xyz = xyz
      
          def __call__(self, *args, **kwargs):
              record = self.original_factory(*args, **kwargs)
              try:
                  record.xyz = thread_local.xyz
              except AttributeError:
                  pass
              return record
      
         def set_xyz(self, xyz):
              thread_local.xyz = xyz
      
      

      这里我创建了一个可调用的类 XYZLogFactory,它记住 xyz 的当前值是什么,也记住原始 LogRecordFactory 是什么。当作为函数调用时,它使用原始 LogRecordFactory 创建一条记录,并使用当前值添加一个 xyz 属性。 thread_local 是为了使其成为线程安全的,但对于更简单的版本,您可以只使用 XYZLogFactory 上的属性:

      class XYZLogFactory():
          def __init__(self, original_factory, xyz):
              self.original_factory = original_factory
              self.xyz = xyz
      
          def __call__(self, *args, **kwargs):
              record = self.original_factory(*args, **kwargs)
              record.xyz = self.xyz
              return record
      
         def set_xyz(self, xyz):
              self.xyz = xyz
      

      在我的第一次尝试中(此处未显示),我没有存储原始工厂,而是使用闭包将其隐式存储在新的 LogRecordFactury 中。但是,过了一会儿,导致了 RecursionError,因为它一直在调用之前的工厂,而后者又调用了之前的工厂,等等。

      关于你的最后一个问题:没有括号,因为这里没有调用函数。相反,它被传递给 logging.setLogRecordFactory,它将它保存在某个变量中,然后在其他地方调用它。如果您想了解更多信息,您可以在 Google 上搜索“作为一等公民的职能”之类的内容。 简单的例子:

      x = str  # Assign to x the function that gives string representation of object
      x(1)  # outputs the string representation of 1, same as if you'd called str(1)
      > '1'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-15
        • 2018-10-06
        • 1970-01-01
        相关资源
        最近更新 更多