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

问题描述 投票:0回答:7

如果我想要函数名称,我只需在格式化程序中包含

%(funcName)s
即可。但是我如何获取包含日志记录调用的类的名称呢?

我已经浏览了

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

python class logging
7个回答
34
投票

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

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

5
投票

你应该使用额外参数:

views.py

import logging

class SampleClass():
    def sample_func(self):
        logging.getLogger('info_logger').info('some text', extra={'className': self.__class__.__name__})

logger_settings.py

'format': '%(className)s | %(message)s ',

输出日志:

INFO | SampleClass | "some text" 

4
投票

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

class MyClass(object):
    @property
    def logger(self):
        return logging.getLogger(f"{__name__}.{self.__class__.__name__}")

    def what(self, ever):
        self.logger.info("%r", ever)

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

0
投票

我个人倾向于在课程后命名我的记录器,因为这样可以更轻松地追踪特定消息的来源。因此,您可以有一个名为“top”的根记录器,对于模块“a”和类“testclass”,我将记录器命名为“top.a.testclass”。

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

@ed 的上述回复,对我来说感觉非常不Pythonic,而且我不愿意在生产代码中使用它。


0
投票

这是一个使用表示类方法创建信息丰富的日志消息的函数:

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
投票

只需创建名称为

f"{__class__.__module__}:{__class__.__qualname__}"
的记录器即可。为了访问
__class__
,需要在方法中定义记录器。我们可以使用
@staticmethod
@cache
来装饰该方法以进行重用。

from functools import cache
import logging


class MyClass:
    @staticmethod
    @cache
    def logger():
        return logging.getLogger(f"{__class__.__module__}.{__class__.__qualname__}")

    def __init__(self):
        __class__.logger().info("Initializing MyClass")

class MyClass2(MyClass):    
    @staticmethod
    @cache
    def logger():
        return logging.getLogger(f"{__class__.__module__}.{__class__.__qualname__}")

    def __init__(self):
        super().__init__()
        __class__.logger().info("Initializing MyClass2")

现在测试一下:

logging.basicConfig(level=logging.INFO)
MyClass()

我们将看到日志消息包含类名称。

INFO:__main__.MyClass:Initializing MyClass

因为记录器是在每个类上定义的静态属性,所以

__class__.logger
将是单独的实例,以便它可以知道记录器是在哪个类中定义的。

logging.basicConfig(level=logging.INFO)
MyClass2()
INFO:__main__.MyClass:Initializing MyClass
INFO:__main__.MyClass2:Initializing MyClass2
© www.soinside.com 2019 - 2024. All rights reserved.