【问题标题】:How do I get the name of the class containing a logging call in Python?如何在 Python 中获取包含日志记录调用的类的名称?
【发布时间】:2011-09-12 08:18:09
【问题描述】:

如果我想要函数名,我可以简单地在格式化程序中包含%(funcName)s。但是如何获取包含日志调用的类的名称呢?

我浏览了logging 的文档,但找不到任何提及它的内容。

【问题讨论】:

  • 默认情况下类名不可用的原因是,虽然函数名可以从堆栈上的框架对象中轻松获得 - f.f_code.co_name - 但类名不可用。获取类名会带来比相应好处更大的运行时损失 - 毕竟,您已经可以准确地看到调用来自哪个文件和行,这比仅使用类更精确。

标签: python class logging


【解决方案1】:

要使用一种相当简单的 Pythonic 方式获取类名以使用您的记录器输出,只需使用日志记录类。

import logging


# Create a base class
class LoggingHandler:
    def __init__(self, *args, **kwargs):
        self.log = logging.getLogger(self.__class__.__name__)


# Create test class A that inherits the base class
class testclassa(LoggingHandler):
    def testmethod1(self):
        # call self.log.<log level> instead of logging.log.<log level>
        self.log.error("error from test class A")


# Create test class B that inherits the base class
class testclassb(LoggingHandler):
    def testmethod2(self):
        # call self.log.<log level> instead of logging.log.<log level>
        self.log.error("error from test class B")


testclassa().testmethod1()
testclassb().testmethod2()

通过如上所述命名记录器,%(name)s 将成为您的类的名称

示例输出

$ python mymodule.py
[2016-02-03 07:12:25,624] ERROR [testclassa.testmethod1:29] error from test class A
[2016-02-03 07:12:25,624] ERROR [testclassb.testmethod2:36] error from test class B

替代方案

非继承

import logging


def log(className):
    return logging.getLogger(className)


class testclassa:
    def testmethod1(self):
        log(self.__class__.__name__).error("error from test class A")


class testclassb:
    def testmethod2(self):
        log(self.__class__.__name__).error("error from test class B")


testclassa().testmethod1()
testclassb().testmethod2()

【讨论】:

  • 和 Mixin 一样好用。
【解决方案2】:

几乎可以肯定有一种更好的方法可以做到这一点,但在有人指出之前,这将起作用:

import inspect

class testclass:
    def testmethod(self):
        log()

def log():
    stack = inspect.stack()
    try:
        print "Whole stack is:"
        print "\n".join([str(x[4]) for x in stack])
        print "-"*20
        print "Caller was %s" %(str(stack[2][4]))
    finally:
        del stack

testclass().testmethod()

输出如下:

Whole stack is:
['    stack = inspect.stack()\n']
['        f()\n']
['testclass().testmethod()\n']
['                exec code in self.locals\n']
['            ret = method(*args, **kwargs)\n']
None
--------------------
Caller was ['testclass().testmethod()\n']

【讨论】:

  • 是的,我也一直在玩 inspect。但感觉非常不合时宜。如果有办法在logging 中做同样的事情,那就太好了。我真的想不出为什么该功能不应该存在。
【解决方案3】:

我个人倾向于以类来命名我的记录器,因为这样更容易追踪特定消息的来源。所以你可以有一个名为“top”的根记录器,对于模块“a”和类“testclass”,我将我的记录器命名为“top.a.testclass”。

我认为没有必要以其他方式检索类名,因为日志消息应该为您提供所需的所有信息。

@ed 上面的回复,我觉得这很不合 Python,而且我不喜欢在生产代码上使用它。

【讨论】:

  • 我正在为库使用本地日志记录实例,但我从未在单个类中将它们用作本地。我想这是最pythonic的方式。但是我仍然没有真正看到为什么类信息不应该出现在函数信息旁边的原因。
  • 当记录到单个日志文件时,使用本地记录器很棘手。
【解决方案4】:

这是一个使用表示类方法制作信息日志消息的函数:

https://docs.python.org/3/library/functions.html#repr

def log_message(thing: object = None, message: str = '') -> str:
    """:returns: detailed error message using reflection"""
    return '{} {}'.format(repr(thing), message)

这可以使用 mix-in 实现到任何类:

class UtilMixin(object):
    def log(self, message: str = '') -> str:
        """:returns: Log message formatting"""
        return log_message(thing=self, message=message)

您可以使用多重继承与一个类关联:

class MyClass(object, UtilMixin):
    def __repr__(self) -> str:
        return '<{}>'.format(self)
    pass

用法

logger.warning(self.log('error message goes here'))

【讨论】:

    【解决方案5】:

    如果您还想要模块名称,还有另一种方法:

    class MyClass(object):
        @property
        def logger(self):
            return logging.getLogger(f"{__name__}.{self.__class__.__name__}")
    
        def what(self, ever):
            self.logger.info("%r", ever)
    

    【讨论】:

      猜你喜欢
      • 2017-12-20
      • 1970-01-01
      • 2019-05-14
      • 1970-01-01
      • 2021-05-12
      • 2022-10-04
      • 1970-01-01
      • 1970-01-01
      • 2016-09-05
      相关资源
      最近更新 更多