Python中的每文件/模块记录器

时间:2013-07-17 20:59:19

标签: python logging

我有一些我需要添加日志记录的Python代码。

我总是喜欢很好的大C宏看法语句,如“DEBUG()”,“ERROR()”等用于记录。我觉得当跟踪点在视觉上与实际代码区分开来时,它使代码更容易阅读(没有对象)。

我还希望能够在每个模块级别设置日志记录级别。

我怎样才能创建一个名为“log”的模块,它能够做到这一点(在使用Python标准库记录模块的同时)?

E.g:

档案:main.py

# This imports LOG_MODULE_NAME, DEBUG, WARN, etc
from log import *
import my_module

LOG_MODULE_NAME("main")

log.set_level("main", log.LVL_DEBUG)
log.set_level("my_module", log.LVL_WARN)

if __name__ == "__main__":
    foo = my_module.myFunc(2)

DEBUG("Exiting main.py")

文件:my_module.py

from log import *

LOG_MODULE_NAME("my_module")


def myFunc(x):
    DEBUG("Entering function")
    if x != 1:
         WARN("I thought it would be 1")
    DEBUG("Exiting function")
    return x+1

我希望输出看起来像:

[WARN:my_module - my_module.py:9] I thought it would be 1
[DEBUG:main - main.py:11] Exiting main.py

4 个答案:

答案 0 :(得分:6)

这些答案似乎跳过了一个非常简单的想法,即函数是允许的第一类对象:

def a_function(n):
    pass

MY_HAPPY_NAME = a_function
MY_HAPPY_NAME(15) # which is equivalent to a_function(15)

外,我建议您不要这样做。 PEP8 coding conventions被广泛使用,因为生命太短,不足以让我必须以COBOLy的方式读取标识符。

其他一些答案也使用global语句,这在Python中几乎不需要。如果您正在制作模块级记录器实例,那么

import logging

log = logging.getLogger(__name__)

def some_method():
    ...
    log.debug(...)

是使用模块级变量log的完美可行,简洁的方法。你甚至可以做

log = logging.getLogger(__name__)
DEBUG = log.debug

def some_method():
    ...
    DEBUG(...)

但是我保留称之为丑陋的权利。

答案 1 :(得分:4)

要扩展@ 2rs2ts概念,您也可以按照Python logging HOWTO中所示的方式逐个登录。

  

命名记录器时使用的一个很好的约定是在每个使用日志记录的模块中使用模块级记录器,命名如下:

logger = logging.getLogger(__name__) 
     

这意味着记录器名称跟踪包/模块层次结构,并且直观地显示仅从记录器名称记录事件的位置

将所有文件的前两行设为:

import logging
logger = logging.getLogger(__name__) 

然后,您可以轻松自定义每个模块的日志记录。

您可以通过以下方式登录到不同级别:

logger.debug("I just saw this %s" , var )
logger.info("I just saw this %s" , var )
logger.warn("I just saw this %s" , var )

请注意,我将字符串模板变量作为*arg传递,而不是%以立即模板化字符串。如果您的记录器设置为高于您登录的级别,则可以节省CPU周期。 logging文档中有一些类似的技巧。

您可以设置日志记录级别以及在哪里登录您运行的任何应用程序/脚本;记录模块将处理其余部分。

答案 2 :(得分:3)

如果您希望记录器的名称指示使用它的模块,您可以使用logger.getLogger([name])模块函数,并将__name__作为其(可选)参数传递,如所解释的{{ 3}}

如果您想使用DEBUG()之类的名称,请在的每个文件中执行以下操作...

LOG_MODULE_NAME = logging.getLogger(__name__)

def DEBUG(msg):
    global LOG_MODULE_NAME
    LOG_MODULE_NAME.debug(msg)

我不清楚全局命名空间在Python中的实际工作方式...... here

  

每个模块都有自己的“全局”命名空间。

所以我猜你会很好,因为LOG_MODULE_NAME不会在模块之间发生碰撞。

我相信这种方法会为你提供一个日志文件,其中的行将如下所示:

DEBUG:my_module:Entering function
WARN:my_module:I thought it would be 1
DEBUG:my_module:Exiting function
DEBUG:root:Exiting main.py

不是很好吗?你想要this answer模块,它会在程序运行时给你很多关于程序的inpsect。例如,这将为您提供当前行号。

您关于“按模块级别设置日志级别”的说明让我觉得您想要information之类的内容。你可以尝试用这样的方法来解决这个问题:

LOG_MODULE_NAME = logging.getLogger(__name__)
MODULE_LOG_LEVEL = log.LVL_WARN

def DEBUG(msg):
    if MODULE_LOG_LEVEL = log.LVL_DEBUG:
        global LOG_MODULE_NAME
        LOG_MODULE_NAME.debug(msg)

我对logging模块的经验不足以告诉您如何动态地制作每个模块的日志更改级别。

答案 3 :(得分:1)

清洁解决方案可以be found on this other question。更简洁的configure logging for a library方式是在每个模块中使用这几行。

import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())

您的主要脚本(例如我的script.py)负责:


举个例子:

def myFunc(x):
    DEBUG("Entering function")
    if x != 1:
         WARN("I thought it would be 1")
    DEBUG("Exiting function")
    return x+1

和您的预期输出:

[WARN:my_module - my_module.py:9] I thought it would be 1
[DEBUG:main - main.py:11] Exiting main.py

我们如何判断DEBUG("Existing main.py")发生了main.py:11?显而易见的答案是你不能,除非你检查框架并检查调用者是谁。如果要在DEBUG()模块中定义log.py函数,则无法获得正确的记录。函数非常与宏不同,您必须定义一个函数,并且没有内联替换,因此您实际上不再处于同一帧中。

只是为了踢,我用introspection with the inspect module写了一个解决方案 (注意:不要将其作为生产代码运行),但实际情况是,为了回答“这个函数在哪里调用以及在什么线上?”的挑战。有些工作需要完成。

  • bar.py

    # file: bar.py
    # this is just a module to show DEBUG works when used in a different module
    from log import DEBUG
    
    def baz():
        DEBUG('oh hai from baz')
    
  • log.py

    # file: log.py
    import inspect
    import logging
    
    def DEBUG(msg, *args):
        curframe = inspect.currentframe()
        try:
            # http://docs.python.org/2/library/inspect.html#inspect.getouterframes
            _, filename, _, code, _, _ = inspect.getouterframes(curframe, 2)[1]
            logging.debug('[%s:%s] '+msg, filename, code, *args)
        finally:
            del curframe
    
  • 主要文字script.py

    # file: script.py
    # responsible for the logging setup.
    import logging
    from log import DEBUG
    from bar import baz
    
    def foo():
        DEBUG('oh hai')
    
    if __name__ == '__main__':
        logging.basicConfig()
        logging.root.setLevel(logging.DEBUG)
        foo()
        baz()
    

我的解决方案的一个明显问题是我们只使用root记录器:

$ python script.py 
DEBUG:root:[script.py:foo] oh hai
DEBUG:root:[/Users/dnozay/Desktop/bar.py:baz] oh hai from baz

鉴于所有这些要素,我不鼓励重新实施logging中已有的功能。