【问题标题】:Weird stuff happening while importing modules导入模块时发生的奇怪事情
【发布时间】:2011-11-25 21:16:52
【问题描述】:

我不想在这个标题下提出问题,但实际上我不知道发生了什么,所以就这样吧。

我正在做另一个项目,我想在其中使用日志记录模块。代码分布在几个文件中,而不是为单独的文件创建单独的记录器对象,我想创建一个包含内容的 logs.py

import sys, logging

class Logger:
    def __init__(self):
        formatter = logging.Formatter('%(filename)s:%(lineno)s %(levelname)s:%(message)s')
        stdout_handler = logging.StreamHandler(sys.stdout)
        stdout_handler.setFormatter(formatter)
        self.logger=logging.getLogger('')
        self.logger.addHandler(stdout_handler)
        self.logger.setLevel(logging.DEBUG)

    def debug(self, message):
        self.logger.debug(message)

并像(在不同的文件中)一样使用这个类。

import logs
b = logs.Logger()
b.debug("Hi from a.py")
  1. 我剥离了整个问题来在这里提出问题。现在,我有 3 个文件,a.py、b.py 和 main.py。所有 3 个文件都实例化了 logs.Logger 类并打印了一条调试消息。
  2. a.py & b.py 导入“日志”并打印它们的调试消息。
  3. main.py 导入日志,a & b;并打印它自己的调试消息。

文件内容如下:http://i.imgur.com/XoKVf.png

为什么 b.py 的调试信息打印了 2 次,而 main.py 的调试信息打印了 3 次?

【问题讨论】:

标签: python python-2.7 python-module


【解决方案1】:

为记录器指定一个名称,否则您总是使用根记录器。

import sys, logging

class Logger:
    def __init__(self, name):
        formatter = logging.Formatter('%(filename)s:%(lineno)s %(levelname)s:%(message)s')
        stdout_handler = logging.StreamHandler(sys.stdout)
        stdout_handler.setFormatter(formatter)
        self.logger=logging.getLogger(name)
        self.logger.addHandler(stdout_handler)
        self.logger.setLevel(logging.DEBUG)

    def debug(self, message):
        self.logger.debug(message)

http://docs.python.org/howto/logging.html#advanced-logging-tutorial

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

logger = logging.getLogger(__name__)

【讨论】:

  • 嗯。我刚刚发现,如果我使用这种方法,日志中显示的文件名始终是我的模块名。因此,这可能不是使用它的好方法。你能推荐一个更好的方法吗?
  • 用你喜欢的任何字符串代替__name__。但请注意,相同的名称将获得相同的记录器。
【解决方案2】:

logging.getLogger('') 每次调用都会返回完全相同的对象。因此,每次您实例化 Logger(为什么在这里使用旧式类?)时,您都会附加一个处理程序,从而导致打印到另一个目标。由于您的所有目标都指向同一事物,所以最后一次调用 .debug() 将打印到指向 sys.stdout 的三个 StreamHandler 对象中的每一个,从而打印三行。

【讨论】:

  • 但是,在每个文件中,我将 logging.getLogger() 的每个引用保存在一个单独的局部变量中。所以,我并没有真正关注。编辑:好的。我明白了,他们都注册到同一个根记录器
  • logging.getLogger(foo) 将在您每次请求相同的foo 时返回相同的内容。因此,即使您将其保存到不同的变量中,您所保存的是对同一实例的引用。对于正确的日志记录,只需在您的模块中调用logging.getLogger(your_loggin_domain),并在您的main.py 中进行正确的logging 模块配置。你根本不需要自己的 Logger 类。
  • 谢谢。顺便说一句,新式类是什么?
  • 它们扩展了object 类型并支持super 之类的东西。 python.org/doc/newstyle
【解决方案3】:

首先。不要创建自己的 Logger 类。

只需使用现有的logging 配置工具配置现有的记录器类。

第二。每次创建自己的 Logger 类时,您还创建了新的处理程序,然后将新的(复制的)处理程序附加到根记录器。这会导致消息重复。

如果您有多个模块必须 (1) 独立运行并且 (2) 还作为更大的复合应用程序的一部分运行,则需要执行此操作。这将确保日志记录配置只进行一次。

import logging
logger= logging.getLogger( __file__ ) # Unique logger for a, b or main


if __name__ == "__main__":
    logging.basicConfig( stream=sys.stdout, level=logging.DEBUG, format='%(filename)s:%(lineno)s %(levelname)s:%(message)s' )
    # From this point forward, you can use the `logger` object.
    logger.info( "Hi" )

【讨论】:

  • 谢谢。我将更改我的代码并使用它。我应该在一开始就给出构造函数中的所有选项。我唯一想做的就是保存行。
  • @shadyabhi:试图“保存行”?这是库模块的两行和主模块的第三行。你怎么能把它变小?
  • 我的意思是我没有像你那样使用构造函数。
  • @shadyabhi:“我没有使用构造函数”?那是什么意思? getLogger() 函数?还是basicConfig() 函数?您是否试图通过编写(测试和调试)一个完整的类来节省一行代码?
  • 很抱歉不清楚。我只想说,我制作一个单独的 logs.py 文件的原因是我想节省几行代码。在那个 logs.py 中,我正在使用单独的函数设置日志记录模块的选项,这些函数占用了这么多行。在我看到您使用构造函数提供大多数选项的编码风格后,我喜欢它。因此,我更改了代码以像您的风格一样实现它。 github.com/shadyabhi/SMSSender/blob/master/SMSSender.py
猜你喜欢
  • 2013-02-27
  • 1970-01-01
  • 1970-01-01
  • 2012-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-19
  • 1970-01-01
相关资源
最近更新 更多