在哪里配置日志记录?

Where to configure logging?

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

例如,我写了一些 class 并且我想在执行方法时记录一些信息。我应该在模块顶部的 init 或更高版本 class 中配置日志记录:

# 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

最佳做法是什么?

日志包有两个用途,协助作者生产日志和协助用户消费日志。您可能同时扮演这两个角色,但分别考虑它们将有助于您编写干净、易于理解的代码。

作者的担忧

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

  • 包记录器应该放在包 __init__ 文件中。注意 __name__ 的使用,它会解析为 SomePackage:

      import logging
      package_logger = logging.getLogger(__name__)
    
  • 模块顶部的模块记录器。注意 __name__ 的威力!在这里它将解析为 SomePackage.SomeModule.

      import logging
      module_logger = logging.getLogger(__name__)
    
  • Class 级别记录器可以放在 class 定义的顶部。请注意 getLogger 和 __qualname__ 增强的 __name__ 的强大功能!记录器名称将为 SomePackage.SomeModule.SomeClass。此外,不是 _class_logger 中的下划线表示它供内部使用。:

      class SomeClass:
          _class_logger = logging.getLogger(__name__).getChild(__qualname__)
    
  • classes __init__ 中的实例记录器。使用 ID 生成唯一标识符。注意这个傻瓜……你明白了。记录器名称将为 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 会将warning及以上级别的所有日志记录转储到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(),它们应该放在那里。这确保包用户不会收到意外的日志文件或控制台消息。

加分

如果在多个级别使用记录器,从现有记录器派生更符合 pythonic。

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)))