【问题标题】:Alter python logger stack level更改 python 记录器堆栈级别
【发布时间】:2018-04-23 17:56:47
【问题描述】:

在编写框架代码时,我经常希望记录调用者的行号和文件名。例如,如果我检测到对框架级 API 调用的不当使用,我想将其记录为……不是框架内错误,而是“调用者错误”。

这只有在编写使用自省的低级库和系统时才会发挥作用。

有没有办法让记录器记录“上一级”?我可以创建一个自定义 LogRecord,然后修改它并以某种方式在已安装的记录器中使用它吗?试图弄清楚这一点。

例如这样的:

def set(self, x):
   if x not in self._cols:
      log.error("Invalid attribute for set", stack_level=-1)

【问题讨论】:

    标签: python logging introspection


    【解决方案1】:

    自从它被添加到python 3.8以来,现在很容易在谷歌上找到它,并且在另一个答案中提到了它,但这里有一个更好的解释。你很接近stack_level 参数,但它应该是stacklevel,值应该是2。例如你可以这样做:

    import logging
    
    def fun():
        logging.basicConfig(format='line %(lineno)s : %(message)s')
        log = logging.getLogger(__name__)
        log.error('Something is wrong', stacklevel=2)
    
    if __name__ == '__main__':
        fun()
    

    这将输出:

    line 9 : Something is wrong
    

    如果堆栈级别设置为默认的1,它将输出:

    line 6 : Something is wrong
    

    来自文档:https://docs.python.org/3/library/logging.html#logging.Logger.debug

    第三个可选关键字参数是stacklevel,默认为1。如果大于1,则在计算为日志事件创建的LogRecord中设置的行号和函数名时跳过相应的堆栈帧数。这可以用于记录助手,以便记录的函数名、文件名和行号不是助手函数/方法的信息,而是其调用者的信息。此参数的名称反映了警告模块中的等效名称。

    【讨论】:

      【解决方案2】:

      我和 Erik 做了同样的事情,但有一些变化:

      import logging
      import inspect
      from os import path
      
      loggers_dict = {}
      
      
      def myMakeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
          if extra and 'pathname' in extra:
              fn = extra.pop('pathname')
          rv = logging.LogRecord(name, level, fn, lno, msg, args, exc_info, func)
          if extra is not None:
              rv.__dict__.update(extra)
          return rv
      
      
      logging.Logger.makeRecord = myMakeRecord
      
      
      def getLogger():
          fn, lno, func = base_logger.findCaller()
          extras = {'pathname': fn, 'lineno': lno, 'funcName': func}
          fnn = path.normcase(fn)
          caller_name = inspect.modulesbyfile.get(fnn, inspect.getmodule(None, fn).__name__)
      
          if caller_name not in loggers_dict:
              loggers_dict[caller_name] = logging.getLogger(caller_name)
          return loggers_dict[caller_name], extras
      
      
      def myLogDebug(*msg):
          log, extras = getLogger()
          if len(msg) == 1:
              log.debug(msg[0], extra=extras)
          else:
              log.debug(' '.join(map(str, msg)), extra=extras)
      

      这里的主要内容是遗留问题,人们在代码上调用了 myLogDebug,如果我更改所有内容,那将是一团糟。另一个问题是 python 2.7 版本,如果我可以使用来自this thread 的参数堆栈级别会很好。

      然后我修改了一些东西以获得正确的调用者堆栈帧级别,并且没有改变原始方法的任何内容。

      编辑 - 1

      caller_name = inspect.modulesbyfile.get(fnn, inspect.getmodule(None, fn).__name__)

      这部分是一个小技巧,只是为了不要一直从检查模块运行getmodule。有一个 dict(一个内部缓存 modulesbyfile)在第一个 getmodule 之后可以直接访问模块名称。

      有时调试源代码帮助的轨道,它没有记录。

      【讨论】:

        【解决方案3】:

        好的,我想通了。

        首先,您需要使用检查来获取框架。然后用信息修改日志中的 extra= 参数。但是您还必须覆盖 makerecord 以防止不适当的保护措施阻止日志工作文件名和行号覆盖。

        def myMakeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, sinfo):
            rv = logging.LogRecord(name, level, fn, lno, msg, args, exc_info, func, sinfo)
            if extra is not None:
                rv.__dict__.update(extra)
            return rv
        
        def mylog(logger, msg, level, *args)
            logging.Logger.makeRecord = myMakeRecord
            frame = inspect.currentframe()
            caller = frame.f_back
            override = {
                "lineno":caller.f_lineno,
                "filename":os.path.basename(caller.f_code.co_filename),
            }
            logger.log(level, msg, extra=override, *args)
        

        奇怪的是,当 extra 和 sinfo 的默认值为 none 时(就像它们在原始定义中所做的那样),我无法让它工作。也许 myMakeRecord 应该使用 *args.... 但这需要抓住 extra = args[9] ... 这很奇怪....但可能不那么糟糕(更有可能是未来的证明)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多