如果我想要函数名称,我只需在格式化程序中包含
%(funcName)s
即可。但是我如何获取包含日志记录调用的类的名称呢?
我已经浏览了
logging
的文档,但我找不到任何提及它的内容。
要使用一种相当简单的 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()
你应该使用额外参数:
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"
如果您还想要模块名称,还有另一种方法:
class MyClass(object):
@property
def logger(self):
return logging.getLogger(f"{__name__}.{self.__class__.__name__}")
def what(self, ever):
self.logger.info("%r", ever)
几乎肯定有更好的方法来做到这一点,但直到有人指出这一点,这才会起作用:
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']
我个人倾向于在课程后命名我的记录器,因为这样可以更轻松地追踪特定消息的来源。因此,您可以有一个名为“top”的根记录器,对于模块“a”和类“testclass”,我将记录器命名为“top.a.testclass”。
我认为没有必要以其他方式检索类名,因为日志消息应该为您提供所需的所有信息。
@ed 的上述回复,对我来说感觉非常不Pythonic,而且我不愿意在生产代码中使用它。
这是一个使用表示类方法创建信息丰富的日志消息的函数:
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'))
只需创建名称为
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