在python中创建分层记录器

时间:2017-08-23 17:51:44

标签: python logging

Python日志记录模块允许用户以分层方式创建记录器。虽然在高层次上理解它,但是当我将代码分布在多个类中并且实际的函数流将创建一个复杂的网格时,我很难实现这样的层次结构。

我可以使用以下代码执行简单的层次结构:

import logging 

logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s] %(childname)s  %(message)s") 

rootLogger = logging.getLogger() 
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)  
rootLogger.setLevel(logging.INFO) 
rootLogger.info("Hi..",extra={'childname':' '})
cL1= rootLogger.getChild("Child1") 
cL1.info("Hello..",extra={'childname': cL1.name})
cL2 = cL1.getChild('Child2') 
cL2.info("Hi .. hello..",extra={'childname':cL2.name})

上述代码块的输出将是

In [8]: rootLogger.info("Hi..",extra={'childname':' '})
2017-08-23 22:52:47,703 [INFO ]    Hi..

In [10]: cL1.info("Hello..",extra={'childname': cL1.name})
2017-08-23 22:52:47,999 [INFO ] Child1  Hello..

In [12]: cL2.info("Hi .. hello..",extra={'childname':cL2.name})
2017-08-23 22:52:48,296 [INFO ] Child1.Child2  Hi .. hello..

所以我的额外参数我能够将整个层次结构转储到日志中。

现在,如何在跨多个文件跨多个类拆分代码时如何实现这一目标?

moduleA.py

class A:
    def __init__(self,name):
        self.name=name
    def doSomethingForA_1(self,**kwargs):
        self.logger(msg="Yes i did A_1",level=logging.INFO)
    def doSomethingForA_2(self,**kwargs):
        self.logger(msg="Yes i did A_2",level=logging.INFO)
    def logger(msg,level):
        '''How do i get the parent logger to create the child logger here?'''
        logger=parentLogger.getLogger(self.name)
        logger.log(msg,level,extra={'childname':self.name})

moduleAggregationType1.py

class AggregatorType1:
    def __init__(self,name):
        self.name=name
        self.objs = []
    def doAggregationType1(self,**kwargs):
        self.logger(msg="Starting aggregation Operation-1",level=logging.INFO)
        for obj in self.objs:
            obj.doSomethingForA_1()
    def logger(msg,level):
        '''How do i get the parent logger to create the child logger here?'''
        logger=parentLogger.getLogger(self.name)
        logger.log(msg,level,extra={'childname':self.name})

moduleAggregationType2.py
class AggregatorType2:
    def __init__(self,name):
        self.name=name
        self.objs = []
    def doAggregationType2(self,**kwargs):
        self.logger(msg="Starting aggregation Operation-2",level=logging.INFO)
        for obj in self.objs:
            obj.doSomethingForA_2()
    def logger(msg,level):
        '''How do i get the parent logger to create the child logger here?'''
        logger=parentLogger.getLogger(self.name)
        logger.log(msg,level,extra={'childname':self.name})

使用这些模块说我通过导入脚本来编写脚本。

agg_1 = AggregatorType1(name='agg_1')

agg_1.objs = [A(name='aType1_1'),A(name='aType1_2'),A(name='aType1_3')]

agg_2 = AggregatorType1(name='agg_1')

agg_2.objs = [A(name='aType2_1'),A(name='aType2_2'),A(name='aType2_3')]

在这种情况下,我的日志记录应该是:

2017-08-23 22:52:47,999 [INFO ] agg_1 Starting aggregation Operation-1
2017-08-23 22:52:47,999 [INFO ] agg_1.aType1_1 Yes i did A_1
2017-08-23 22:52:47,999 [INFO ] agg_1.aType1_2 Yes i did A_1
2017-08-23 22:52:47,999 [INFO ] agg_1.aType1_3 Yes i did A_1
2017-08-23 22:52:47,999 [INFO ] agg_2 Starting aggregation Operation-2
2017-08-23 22:52:47,999 [INFO ] agg_2.aType2_1 Yes i did A_2
2017-08-23 22:52:47,999 [INFO ] agg_2.aType2_2 Yes i did A_2
2017-08-23 22:52:47,999 [INFO ] agg_2.aType2_3 Yes i did A_2

因此,在执行每个方法时,我应该确定我的日志应该传播到哪个父记录器。

现在我们可以为所有可调用方法设置一个装饰器,我们可以确定其中的父记录器,并创建一个子记录器并在装饰器的末尾删除它。但问题仍然存在..

如何在每个方法水平的运行时间确定父母记录器?

1 个答案:

答案 0 :(得分:0)

简单的答案你不能。没有"父记录器"这样的东西。 python日志记录系统承诺的是,您可以从解释器进程中的任何位置通过名称引用相同的记录器。

所以你的选择(我认为):

1)在构造变量时静态声明记录的位置(这很丑陋且容易出错)

2)使用内置%(module)s%(funcName)s修饰符。为此,您将不得不停止使用自己的logger方法,因为它将掩盖您在其中的方法的实际名称

3)使用traceback模块在​​运行时获取堆栈跟踪

我已修改了一些代码以反映所有这些更改(并在途中删除了可选参数)

class A():
    def __init__(self, name):
        logger = logging.getLogger(name)
        self.log = logger.log
    def doSomethingForA_1(self,**kwargs):
        self.log(msg="Yes i did A_1",level=logging.INFO)
    def doSomethingForA_2(self,**kwargs):
        stack = traceback.extract_stack(limit=2)
        self.log(msg='Caller=%s:%d>%s>'%(stack[0][0:3]), level=logging.INFO)

在main中:

logFormatter = logging.Formatter("[%(levelname)s] %(name)s <%(module)s.%(funcName)s>  %(message)s")


agg_1 = AggregatorType1(name='agg_1')
agg_2 = AggregatorType2(name='agg_2')

agg_1.objs = [A(name='agg_1.aType1_1'),A(name='agg_1.aType1_2'),A(name='agg_1.aType1_3')]
agg_2.objs = [A(name='agg_2.aType2_1'),A(name='agg_2.aType2_2')]

agg_1.doAggregationType1()
agg_2.doAggregationType2()

这将提供以下输出:

[INFO] root <main.<module>>  Hi..
[INFO] agg_1 <moduleAggregationType1.doAggregationType1>  Starting aggregation Operation-1
[INFO] agg_1.aType1_1 <moduleA.doSomethingForA_1>  Yes i did A_1
[INFO] agg_1.aType1_2 <moduleA.doSomethingForA_1>  Yes i did A_1
[INFO] agg_1.aType1_3 <moduleA.doSomethingForA_1>  Yes i did A_1
[INFO] agg_2 <moduleAggregationType2.logger>  Starting aggregation Operation-2
[INFO] agg_2.aType2_1 <moduleA.doSomethingForA_2>  Caller=<moduleAggregationType2.py:11.doAggregationType2>
[INFO] agg_2.aType2_2 <moduleA.doSomethingForA_2>  Caller=<moduleAggregationType2.py:11.doAggregationType2>