5

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

我一直更喜欢漂亮的大 C 宏外观语句,如“DEBUG()”、“ERROR()”等来进行日志记录。我觉得当跟踪点与实际代码在视觉上可以区分时,它使代码更易于阅读(没有对象)。

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

我如何制作一个能够做到这一点的名为“log”的模块(同时使用 Python 标准库日志记录模块)?

例如:

文件: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

4 回答 4

6

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

def a_function(n):
    pass

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

除非我建议您不要这样做。PEP8 编码约定被广泛使用,因为生命太短了,我不得不以你喜欢的 COBOLy 方式来读取标识符。

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

import logging

log = logging.getLogger(__name__)

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

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

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

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

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

于 2013-07-18T00:35:20.743 回答
4

要扩展 @2rs2ts 概念,您也可以按模块记录,如Python logging HOWTO中所示。

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

logger = logging.getLogger(__name__) 

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

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

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,而不是 a%以立即对字符串进行模板化。如果您的记录器设置为高于您登录的级别,您可以节省 CPU 周期。logging文档中有一些类似的技巧。

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

于 2013-07-17T21:22:36.443 回答
3

如果您想让记录器的名称指示使用它的模块,您可以使用模块函数,并作为其(可选)参数logger.getLogger([name])传递,如此所述。__name__

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

LOG_MODULE_NAME = logging.getLogger(__name__)

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

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

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

所以我想你会很好,因为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

不够内省?您需要该inpsect模块,它会在程序运行时为您提供大量有关程序的信息。例如,这将为您获取当前行号。

您关于“在每个模块级别设置日志级别”的注释让我认为您想要类似getEffectiveLevel(). 您可以尝试像这样将其弄脏:

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模块的经验还不够,无法告诉您如何能够动态地使每个模块的日志更改级别。

于 2013-07-17T21:10:04.680 回答
1

可以在其他问题上找到更清洁的解决方案。为库配置日志记录的更简洁方法是在每个模块中使用这几行。

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

您的主脚本(例如 my 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,那么您将无法获得正确的记录。函数与宏非常不同,您必须定义一个函数并且没有内联替换,因此您实际上不再处于同一框架中。

只是为了好玩,我使用模块的自省inspect编写了一个解决方案 (注意:不要将其作为生产代码运行),但现实是为了回答挑战“这个函数在哪里调用以及在哪一行?” 需要做一些工作。

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

于 2013-07-17T23:32:52.547 回答