简体   繁体   English

在python中创建分层记录器

[英]Creating Hierarchical loggers in python

The Python logging module allows users to create loggers in a hierarchical fashion. Python日志记录模块允许用户以分层方式创建记录器。 Though in understand it at a high level i have difficulty in implementing such hierarchies when i code is distributed across multiple classes and the actual function flow will create a complex mesh.. 虽然在较高层次上理解它,但是当我的代码分布在多个类中并且实际的函数流将创建一个复杂的网格时,我很难实现这样的层次结构。

I am able to do a simple hierarchy with the below code: 我可以使用以下代码做一个简单的层次结构:

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

The output of the above code block will be 上面代码块的输出将是

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..

So my with the extra parameter i am able to dump the whole hierarchy in the log. 因此,使用额外的参数,我可以将整个层次结构转储到日志中。

Now how to achieve this when the code is split across multiple classes, across multiple files? 现在,当将代码拆分为多个类,多个文件时,如何实现呢?

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

With these modules say i write a script by importing them. 使用这些模块,我可以通过导入来编写脚本。

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

In this case my logging should be: 在这种情况下,我的日志应该是:

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

So at the time of execution of each method i should be determining what is my parent logger to which my log should be propagated.. 因此,在执行每种方法时,我应该确定日志应传播到的父记录器是什么。

Now we can have a decorator for all the callable methods and we can determine the parent logger in that and create a child logger and delete it at the end of the decorator. 现在,我们可以为所有可调用方法提供一个装饰器,并可以在其中确定父记录器,并创建一个子记录器,并在装饰器的末尾将其删除。 But still the question remains.. 但是问题仍然存在。

HOW TO DETERMINE THE PARENT LOGGER AT RUN TIME AT EACH METHOD LEVEL? 如何以每种方法级别在运行时确定父记录器?

Plain answer you can not. 不能简单回答。 There is no such thing as "parent logger". 没有“父记录器”之类的东西。 What python logging system promises is that you can reference by name the same logger from anywhere in your interpreter process. python日志记录系统承诺的是,您可以在解释器过程中的任何位置按名称引用同一记录器。

So your options are (as I see it): 因此,您的选择是(如我所见):

1) Either statically declare where to log when constructing variable (which is ugly and error prone) 1)在构造变量时静态声明要记录的位置(这很丑陋且容易出错)

2) use built-in %(module)s and %(funcName)s modifiers. 2)使用内置的%(module)s%(funcName)s修饰符。 For that you will have to stop using your own logger method, as it will mask off actual name of method you are inside of 为此,您将不得不停止使用自己的logger方法,因为它将掩盖您所在方法的实际名称。

3) use traceback module for getting your stack trace in runtime 3)使用traceback模块在运行时获取堆栈跟踪

I have modified a little bit your code to reflect all those changes (and dropped your optional parameter on the way) 我对您的代码做了一些修改以反映所有这些更改(并在途中删除了可选参数)

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)

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

This will provide the following output: 这将提供以下输出:

[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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM