2013-07-30 80 views
2

我可以创建一个名为子记录,使所有输出的日志,通过记录器都标有它的名字。我可以只在我的函数/类/任何情况下使用该记录器。但是,如果该代码调用另一个模块中的函数,该模块仅使用日志记录模块函数(代理向根记录器)进行日志记录,那么如何确保这些日志消息通过同一个记录器(或者至少以同样的方式登录)?如何使日志模块功能使用不同的日志记录器?

例如:

main.py

import logging 

import other 

def do_stuff(logger): 
    logger.info("doing stuff") 
    other.do_more_stuff() 

if __name__ == '__main__': 
    logging.basicConfig(level=logging.INFO) 
    logger = logging.getLogger("stuff") 
    do_stuff(logger) 

other.py

import logging 

def do_more_stuff(): 
    logging.info("doing other stuff") 

输出:

$ python main.py 
INFO:stuff:doing stuff 
INFO:root:doing other stuff 

我希望能够引起两个日志行用名字'东西'来标记',我希望能够做到这一点只改变main.py.

我怎样才能引起other.py记录呼叫使用不同的记录,而不改变该模块?

回答

1

这是解决方案,我想出来的:

使用线程本地数据存储上下文信息,并使用根记录器处理一个筛选器,在发射前,将此信息添加到的LogRecords。

context = threading.local()              
context.name = None                

class ContextFilter(logging.Filter):            

    def filter(self, record):              
     if context.name is not None:            
      record.name = "%s.%s" % (context.name, record.name)    
     return True                

这对我来说很好,因为我使用记录器名称来指示记录此消息时正在执行的任务。

然后,我可以使用上下文管理器或装饰器从特定代码段进行日志记录,就像它是从特定的子记录器记录一样。

@contextlib.contextmanager              
def logname(name):                
    old_name = context.name              
    if old_name is None:               
     context.name = name              
    else:                   
     context.name = "%s.%s" % (old_name, name)         

    try:                     
     yield                   
    finally:                   
     context.name = old_name              

def as_logname(name):                
    def decorator(f):                
     @functools.wraps(f)              
     def wrapper(*args, **kwargs):            
      with logname(name):             
       return f(*args, **kwargs)           
     return wrapper               
    return decorator                

那么,我可以这样做:

with logname("stuff"): 
    logging.info("I'm doing stuff!") 
    do_more_stuff() 

或:

@as_logname("things") 
def do_things(): 
    logging.info("Starting to do things") 
    do_more_stuff() 

关键的一点是,任何日志记录do_more_stuff()没有将被记录,就好像它是用记录或者一个“东西”或“事物”子记录器,根本不需要更改do_more_stuff()

如果您要在不同的子记录器上使用不同的处理程序,则此解决方案会有问题。

+0

似乎自恋接受我自己的答案,但我这样做,因为这是我使用的解决方案。仍然打开更好的解决方案。 – SpoonMeiser

0

使用logging.setLoggerClass以便被其他模块使用的所有记录仪使用记录仪子类(重点煤矿):

告知日志系统实例化一个记录器时要使用的一个Klass类。该类应该定义__init__(),从而仅需要一个名称参数,以及__init__()应该叫Logger.__init__()该函数通常调用的任何记录仪由需要使用定制记录器的行为应用实例化之前。

+0

你能举一个例子来解答这个问题吗? – SpoonMeiser

+0

也许有一些特殊的用例,但99%的时间,它只是一个添加处理程序的问题。 – tdelaney

+0

此外,它设置了子记录器的类,但不允许您控制根记录器。因此,这不会影响诸如'logging.info(“hello”)'这样的调用,因为它们将使用根记录器。 – SpoonMeiser

-1

logging.{info,warning,…}方法只是调用Logger对象上设置相应的方法称为root(参见logging module source),所以如果你知道other模块仅调用由logging模块导出的功能,可以覆盖logging模块中other名字空间与您的logger对象:

import logging 

import other 

def do_stuff(logger): 
    logger.info("doing stuff") 
    other.do_more_stuff() 

if __name__ == '__main__': 
    logging.basicConfig(level=logging.INFO) 
    logger = logging.getLogger("stuff") 
    # Overwrite other.logging with your just-generated logger object: 
    other.logging = logger 
    do_stuff(logger) 
+1

不,这不是一个好的解决方案。无需重写模块记录器,也不需要花时间计算模块在哪里存储它们。 – tdelaney

0

这是logging.handlers(或日志模块中的处理程序)是。除了创建记录器之外,还可以创建一个或多个处理程序,以将记录信息发送到各个位置,并将它们添加到根记录器。执行日志记录的大多数模块创建一个记录器,它们用于自己的目的,但取决于控制脚本来创建处理程序。一些框架决定是超级有用的,并为你添加处理程序。

阅读logging docs,它都在那里。

(编辑)

logging.basicConfig()是一个辅助功能,增加了一个单个处理程序到根记录器。您可以使用'format ='参数控制它使用的格式字符串。如果你想要做的就是让所有的模块显示“东西”,然后使用logging.basicConfig(level=logging.INFO, format="%(levelname)s:stuff:%(message)s")

+0

如何添加处理程序使其他模块不能控制以指定格式登录? – agf

+0

@agf,假设一个模块登录“mighty.module”。您创建一个处理程序,向其中添加一个格式化程序,然后将该处理程序添加到记录程序中。通常,你在根记录器(logging.getLogger())上执行此操作,但可以在任何位置执行此操作(logging.getLogger(“mighty.module”)。 – tdelaney

+0

@agf,[logging howto](http:// docs .python.org/2/howto/logging.html#)有很好的例子。 – tdelaney

相关问题