问题 如何在Python中获取包含日志记录调用的类的名称?


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

我已经完成了文档 logging,但我找不到任何提及它。


8781
2017-09-12 08:18


起源

默认情况下类名不可用的原因是,虽然函数名可以从堆栈中的帧对象中获得 - f.f_code.co_name  - 班级名称不是。获取类名将导致运行时惩罚大于相应的好处 - 毕竟,您已经可以确切地看到调用来自哪个文件和行,这比仅仅类更精确。 - Vinay Sajip


答案:


要获得使用记录器输出类名的相当简单,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()

8
2018-02-03 12:45





几乎肯定有更好的方法可以做到这一点,但是直到有人指出这一点,这将有效:

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']

2
2017-09-12 09:14



是的,我一直在玩弄 inspect 太。但感觉非常不合情理。如果有办法做同样的事情,那将是超级的 logging 代替。我无法真正想到为什么不应该存在这种功能的任何原因。 - c00kiemonster
是的我同意 :) - ed.


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

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

@ ed的上面的回答,对我来说感觉非常不合理,而且我不习惯在生产代码上使用它。


0
2017-09-12 14:09



我正在为库使用本地日志记录实例,但我从未在个别类中使用它们作为本地。我想这是最狡猾的做法。但是我仍然没有真正看到为什么类信息不应该出现在功能信息旁边的原因。 - c00kiemonster
记录到单个日志文件时,使用本地记录器非常棘手。 - tashuhka


这是使用表示类方法生成信息性日志消息的函数:

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)

这可以使用混合实现到任何类:

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'))

0
2018-02-19 14:00