在哪里配置日志记录?

19

我想知道在哪里配置和初始化与日志模块相关的内容?

例如,我正在编写某个类,并且我希望在执行方法时记录一些信息。我应该在init中配置日志记录还是在模块顶部的上方配置日志记录:

# LOGGING STUFF <--- Should be here ? 

class SomeClass:

    def __init__(self):
        # class stuff
        # LOGGING STUFF <--- Or should be here ? 

    def some_method(self):
        # method stuff
        # LOGGING SOME INFO    

    def some_method2(self):
        # method stuff
        # LOGGING SOME INFO

最佳实践是什么?

1个回答

28

日志包有两个作用,一是帮助作者生成日志,二是帮助用户消费日志。你可能会同时扮演这两个角色,但将它们分开考虑有助于编写干净、易懂的代码。

作者关注点

作者应该在正确的级别上实例化记录器:

  • Package logger should go in the packages __init__ file. Note the use of __name__, it'll resolve to SomePackage:

      import logging
      package_logger = logging.getLogger(__name__)
    
  • Module logger at the top of your module. Note the power of __name__! Here it'll resolve to SomePackage.SomeModule.

      import logging
      module_logger = logging.getLogger(__name__)
    
  • Class level logger could go at the top of the class definition. Note the awesome power of __name__ enhanced with getLogger and __qualname__! The loggers name will be SomePackage.SomeModule.SomeClass. Also, note the underscore in _class_logger to signal that it is for internal use.:

      class SomeClass:
          _class_logger = logging.getLogger(__name__).getChild(__qualname__)
    
  • Instance logger in the classes __init__. Use ID to produce a unique identifier. Note the stupend... you get the idea. Logger name will be SomePackage.SomeModule.SomeClass.<large_unique_number>:

      class SomeClass:
          def __init__(self):
              self._instance_logger = logging.getLogger(__name__).getChild(self.__class__.__name__).getChild(str(id(self)))
    

这些名称可能不适合您的应用程序。例如,您可能希望从其实例化参数中派生一个实例记录器。但是,您仍应该在正确的级别上把握好您的记录器。

用户关注点

配置处理程序是用户的工作。通常,作者会确保默认情况下没有处理程序处于活动状态。logging.basicConfig将所有警告级别及以上的日志记录转储到stderr。

import SomePackage
import logging

logging.basicConfig()

记住你可以使用logging.getLogger()来访问作者定义的相同记录器。

from SomePackage import SomeModule
import logging

"""
module_logger debug to a file. Will capture all 
child loggers as well if used i.e class_logger and 
instance_logger
"""
h = logging.FileHandler('debug')
h.setLevel('DEBUG')
SomeModule.module_logger.addHandler(h)

"""
class_logger warning to stderr. Will capture all child loggers
as well if used i.e instance_logger Note: No need to import
SomeClass to access it's logger as Logger keeps track of them in
a dictionary. Typically the prefered approach if your not
actually using the class.
"""
h = logging.StreamHandler()
getLogger('SomePackage.SomeModule.SomeClass').addHandler(h)

如果您正在直接调用自己的模块进行调试,那么您可能有一个def main()函数,应该将它们放在里面。这可以确保包用户不会收到意外的日志文件或控制台消息。

额外的标记

如果在多个级别上使用记录器,则派生自现有记录器更具Python特色。

module_logger = logging.getLogger(__name__)

class SomeClass:
    _class_logger = module_logger.getChild(__qualname__)

    def __init__(self):
        self._instance_logger = self._class_logger.getChild(str(id(self)))

两个生命周期不重叠的对象可能具有相同的id()值。uuid4()是获得程序运行期间唯一标识符的更好方法。 - Guy Gangemi
我有一个带有日志记录模块的包。该包在一个名为 def main() 的函数中使用,在这个函数下面我配置了日志记录器。对于一个多次调用 main() 的脚本,有什么最佳实践呢? - Eney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接