Python - 避免在函数之间传递记录器引用?

时间:2011-05-12 06:42:18

标签: python logging

我有一个简单的Python脚本,它使用内置的logging

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

#!/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__内部调用logger。但是,如何从count_parrots()函数内部调用logger?处理这样的记录器的最pythonic方法是什么?

6 个答案:

答案 0 :(得分:39)

您可以使用root(默认)记录器,因此模块级别函数logging.debug,...或使用它来获取函数中的记录器。 实际上,getLogger函数是一个类似工厂的函数,带有一个注册表(类似单例),即它总是返回给定记录器名称的相同实例。 因此,您只需使用

即可将您的记录器输入count_parrots
logger = logging.getLogger("my logger") 

一开始。但是,惯例是为记录器使用点分层名称。见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。

答案 1 :(得分:15)

你可以这样做:

logger = logging.getLogger("my logger") 

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

更新:来自logging tutorial (emphais mine)

  

getLogger()返回对a的引用   具有指定的logger实例   提供名称,如果提供,则为root   不。名称以句点分隔   等级结构。 多个   使用相同的方法调用getLogger()   name将返回对引用的引用   相同的记录器对象。

答案 2 :(得分:8)

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

在文档中的高级日志记录教程的简介中对此进行了简要讨论: http://docs.python.org/howto/logging.html#advanced-logging-tutorial

可以传递记录器实例作为参数,但这样做通常很少见。

答案 3 :(得分: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()

答案 4 :(得分:0)

如果您不需要控制台上的日志消息,则可以以极简主义的方式使用。

或者,您可以使用tail -f myapp.log查看控制台上的消息。

import logging

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', \
    filename='myapp.log', \
    level=logging.INFO)

def do_something():
    logging.info('Doing something')

def main():
    logging.info('Started')
    do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()

答案 5 :(得分:-4)

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