在Python中创建模块级别的日志记录器是好设计吗?

5 投票
3 回答
2106 浏览
提问于 2025-04-16 02:54

在写Python代码的时候,我经常使用日志模块。

经过一些不好的经历和阅读了像这篇文章这样的内容后,我尽量避免在导入时执行代码。

不过为了简单起见,我通常会在模块文件的开头就获取我的日志对象:

# -*- coding: utf-8 -*-
import logging
logger = logging.getLogger('product.plugin.foo.bar')

这样一来,我的日志记录器就可以在全局使用,我可以在任何地方直接写“logger.error()”。另一种做法是把它放在类里面:

class Bar(object):
    logger = logging.getLogger('product.plugin.foo.bar')

但这样的话,我每次都得输入类名。为了避免每次都打类名,我想用“self”来代替,但在静态方法里这样做就会出错。

    def my_method(self):
        Bar.logger.error('foo')

    def my_method_2(self):
        self.logger.error('foo') # ok...

    @staticmethod
    def my_method_2():
        self.logger.error('foo') # boom!

所以,一开始看起来在模块范围内创建日志对象似乎是个好主意,但这样做我又担心会遇到与导入相关的问题……

3 个回答

0

我会在模块的开头创建一个日志记录对象,如果合适的话,我可能还会在子模块中使用它。

不过,如果我觉得有必要的话,我可能还会在一个使用日志记录很多的大类里面再创建一个额外的日志记录对象。例如,当我觉得为这个重要的类单独配置日志的详细程度或者日志处理方式会很有用时(比如一个处理订单的类或者数据库查询的类可能就是这种情况)。

你不用担心你的日志记录对象会被模块外部访问。实际上,Python根本不支持私有成员。我记得Guido van Rossum曾经说过类似的话:“我们都是成年人,不是吗?”

1

哎呀,我在发完那个问题的瞬间就意识到,我的“替代方案”其实没什么不同:日志记录器也是在导入时创建的;)

不过,我还是想听听你们对处理这个问题的最佳方法的看法。第一个解决方案还有一个好处:我们有些情况下需要在try/except块中使用导入语句来检查模块是否可用。通过在文件开头就创建日志记录器,你可以在这类事件发生时就开始记录日志。

9

没问题。我甚至使用同样的变量名 logger。有记录总比没有好,但我觉得最好只暴露 logger 这个变量,把模块隐藏起来,这样你的代码只需要引用 logger,保持模块的命名空间整洁。

如果你以后需要在模块内部调整命名空间,可以在那些类里使用 self.logger,或者在必要时覆盖全局的 logger。

更新0

__all__ = [anything but logger]
import logging
logger = logging.getLogger("why.is.this.method.still.java.camel.case")
del logging

注意一下 S.Lott 的贡献。另外,一般来说,你也不想使用 from x import * 这种写法。

撰写回答