2011-05-12 65 views
33

我有一个简单的Python脚本,使用内置的loggingPython - 避免在函数之间传递记录器引用?

我正在配置函数内的日志记录。基本结构是这样的:

#!/usr/bin/env python 
import logging 
import ... 

def configure_logging(): 
    logger = logging.getLogger("my logger") 
    logger.setLevel(logging.DEBUG) 
    # Format for our loglines 
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 
    # Setup console logging 
    ch = logging.StreamHandler() 
    ch.setLevel(logging.DEBUG) 
    ch.setFormatter(formatter) 
    logger.addHandler(ch) 
    # Setup file logging as well 
    fh = logging.FileHandler(LOG_FILENAME) 
    fh.setLevel(logging.DEBUG) 
    fh.setFormatter(formatter) 
    logger.addHandler(fh) 
    return logger 

def count_parrots(): 
    ... 
    logger.debug?? 

if __name__ == '__main__': 
    logger = configure_logging() 
    logger.debug("I'm a log file") 
    parrots = count_parrots() 

我可以从里面__main__调用记录的罚款。但是,如何从count_parrots()函数内部调用记录器?处理像这样配置记录器的最pythonic方式是什么?

回答

32

您可以使用根(默认)记录器,因此模块级别功能logging.debug,...或使用它的函数中获取您的记录器。 事实上,getLogger函数是一个类似工厂的函数,它具有注册表(单例),即它始终为给定的记录器名称返回相同的实例。 您可以通过简单地使用

logger = logging.getLogger("my logger") 

之初从而获得count_parrots你的记录。但是,惯例是为记录器使用点分层名称。见http://docs.python.org/library/logging.html#logging.getLogger

编辑:

您可以使用一个装饰来记录行为添加到您的个性化功能,例如:

def debug(loggername): 
    logger = logging.getLogger(loggername) 
    def log_(enter_message, exit_message=None): 
     def wrapper(f): 
      def wrapped(*args, **kargs): 
       logger.debug(enter_message) 
       r = f(*args, **kargs) 
       if exit_message: 
        logger.debug(exit_message) 
       return r 
      return wrapped 
     return wrapper 
    return log_ 

my_debug = debug('my.logger') 

@my_debug('enter foo', 'exit foo') 
def foo(a, b): 
    return a+b 

可以“硬编码”记录器名称,取下顶关闭级别和my_debug。

+5

好的,所以我可以在每个需要记录的函数的开头调用logging.getLogger。似乎有点浪费和重复肯定?很公平。或者,我会更好地面向对象,并试图将整个过程整合到一个类中去? (我知道这是一个非常普遍的问题,我只是在寻找Python世界中的成就)。 – victorhooi 2011-05-17 02:30:54

+0

你可以把你的函数放在一个类中,使用logger作为实例变量,或者(我更喜欢)创建一个装饰器,将日志函数添加到你的单个函数中 – 2011-05-19 12:17:16

+9

这个答案显示了Python'logging'几乎所有的错误。模块... – rkrzr 2014-04-30 15:04:17

-3

您可以给logger作为count_parrots()的参数或者,我会做什么,创建类鹦鹉并使用记录器作为其方法之一。

6

处理日志记录的典型方法是将每个模块的记录器存储在全局变量中。该模块中的任何函数和方法都只引用同一个记录器实例。

这是在介绍简要讨论到文档中的提前采伐教程: http://docs.python.org/howto/logging.html#advanced-logging-tutorial

可以通记录身边的实例作为参数,但这样做通常是罕见的。

+0

我认为标准做法是使用logger = logging.getLogger(“logger.name”) – 2011-05-12 07:33:49

+0

在模块级别,当然。尽管使用单独的记录器来处理同一模块中的不同功能和方法通常是矫枉过正的。一个例外是使用单独的记录器可以是一种记录哪些线程记录特定事件的非常简单的方法。 – ncoghlan 2011-05-12 10:50:53

+0

啊。我以为你的意思是使用'global'关键字。 – 2011-05-12 11:08:59

13

你可以这样做:

logger = logging.getLogger("my logger") 

count_parrots()方法。当您传递之前使用的名称(即“我的记录器”)时,日志记录模块将返回与该名称相对应的相同实例。

更新:从logging tutorial (emphais矿)

getLogger()返回如果 不具有指定 名称的引用到 记录器实例,如果提供,或根。这些名称是以期限分隔的 分层结构。多个 调用getLogger()与相同的 名称将返回对同一个记录器对象 的引用。

0

我对Python的全局变量是如何工作感到困惑。在一个函数中,只需要声明global logger,如果您正在执行类似logger = logging.getLogger("my logger")并希望修改全球logger

因此,要修改您的示例,您可以在文件的开头创建一个全局记录器对象。如果您的模块可以由另一个模块导入,则应该添加NullHandler,这样如果库的导入器不需要启用日志记录,那么它们与您的lib没有任何问题(ref)。

#!/usr/bin/env python 
import logging 
import ... 

logger = logging.getLogger("my logger").addHandler(logging.NullHandler()) 

def configure_logging(): 
    logger.setLevel(logging.DEBUG) 
    # Format for our loglines 
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 
    # Setup console logging 
    ch = logging.StreamHandler() 
    ch.setLevel(logging.DEBUG) 
    ch.setFormatter(formatter) 
    logger.addHandler(ch) 
    # Setup file logging as well 
    fh = logging.FileHandler(LOG_FILENAME) 
    fh.setLevel(logging.DEBUG) 
    fh.setFormatter(formatter) 
    logger.addHandler(fh) 

def count_parrots(): 
    ... 
    logger.debug('counting parrots') 
    ... 
    return parrots 

if __name__ == '__main__': 
    configure_logging() 
    logger.debug("I'm a log file") 
    parrots = count_parrots()