根目录下和项目记录器下的层次结构中的Python记录器?

时间:2019-07-13 18:14:00

标签: python python-3.x logging

首先,我读过real python article on the subject

了解到记录器具有层次结构后,我想在这种层次结构中创建一个名为MyProjectLogger的新记录:

Root logger
    · MyProjectLogger
         · File logger 1
         · File logger 2
         · ... and so on with all the loggers in my project...

以便MyProjectLogger用于所有后代记录器,因为现在我在项目中的所有记录器中都使用了具有相同配置的所有相同处理程序(很多)。尽管仅通过一种方法来执行此操作,但感觉并不正确。这样,我只将处理程序添加到MyProjectLogger一次,并且所有后代记录器都将使用MyProjectLogger的处理程序进入层次结构。

我不想为此使用默认的根记录器,因为我正在登录一些第三方库,现在我希望项目中的记录器与库中的记录器分开记录。

因此,总而言之:

  • 我想在层次结构中定义记录器MyProjectLogger
  • 我希望它是root logger的直接后代
  • 我希望它成为源代码中所有记录器的父项
  • 我相信我应该使用propagation = False,以便将处理程序添加到MyProjectLogger并将其处理给后代记录器

我唯一的疑问是:我如何给它起这样一个名字,使得它在根目录下并在其余部分之上?

我知道:

logging.getLogger()  # Gets the root logger
logging.getLogger(__name__)  # Gets a logger for the present file
logging.getLogger(__package__)  # Gets a logger for the present module

所以说,如果我的项目具有以下文件夹布局:

aaaBot/
   main.py  # Only file visible in this example.
            # Please assume the rest of folders have files
   common/
      utils/ 
   config/
   database/
   exceptions/
   model/
   wizards/

在每个文件夹的每个文件中,我都使用logging.getLogger(__name__)。根目录中的__package__返回None,而在主可执行文件main.py中,__name__'__main__'

我应该添加前缀+'。为我项目中的所有记录器,并使用该前缀(例如getLogger(prefix+'.'))创建MyProjectLogger?

如果没有,我该怎么办?

1 个答案:

答案 0 :(得分:1)

这是一个工作示例,它说明了记录器层次结构模仿模块的结构:

so-57021706
└── aaaBot
    ├── __init__.py
    ├── common
    │   ├── __init__.py  # empty
    │   └── utils.py
    └── main.py

来源

aaaBot/__init__.py

import logging
import sys


PKG_LOGGER = logging.getLogger(__name__)


def setup_logging():
    msg_format = '%(asctime)s [%(levelname)8s] %(message)s (%(name)s - %(filename)s:%(lineno)s)'
    date_format = '%Y-%m-%d %H:%M:%S'
    formatter = logging.Formatter(fmt=msg_format, datefmt=date_format)
    console_handler = logging.StreamHandler(stream=sys.stdout)
    console_handler.setLevel(logging.DEBUG)
    console_handler.setFormatter(formatter)
    PKG_LOGGER.addHandler(console_handler)
    PKG_LOGGER.setLevel(logging.DEBUG)
    PKG_LOGGER.propagate = False
    PKG_LOGGER.info('finished logging setup!')

aaaBot/common/utils.py

import logging


UTILS_LOGGER = logging.getLogger(__name__)


def spam():
    UTILS_LOGGER.debug('entered spam() function')
    output = 'eggs'
    UTILS_LOGGER.debug('leaving spam() function')
    return output

aaaBot/main.py

import sys
from aaaBot import setup_logging
from aaaBot.common.utils import spam


if __name__ == '__main__':
    if sys.argv[-1] == '--with-logging':
        setup_logging()
    print(spam())

执行

正常运行:

$ python -m aaaBot.main
eggs

调试运行(打开登录):

$ python -m aaaBot.main --with-logging
2019-07-15 13:16:04 [    INFO] finished logging setup! (aaaBot - __init__.py:18)
2019-07-15 13:16:04 [   DEBUG] entered spam() function (aaaBot.common.utils - utils.py:8)
2019-07-15 13:16:04 [   DEBUG] leaving spam() function (aaaBot.common.utils - utils.py:10)
eggs

说明

在此示例项目中,PKG_LOGGER下的aaaBot/__init__.py是“项目”记录器,名称为aaaBot。它也是唯一配置的记录器。所有子记录器除了将记录传播到PKG_LOGGER以外,什么也不做。子记录器的示例是UTILS_LOOGER中的aaaBot/common/utils.py-未配置,名称为aaaBot.common.utils。在这种情况下,层次结构是:

root logger           code: logging.getLogger()
                      name: "root"
                      configured: no
                      propagates to parent: no (already root)
└── PKG_LOGGER        code: logging.getLogger('aaaBot')
                      name: "aaaBot"
                      configured: yes
                      propagates to parent: no (because of propagate = False)
    └── UTILS_LOGGER  code: logging.getLogger('aaaBot.common.utils')
                      name: "aaaBot.common.utils"
                      configured: no
                      propagates to parent: yes (default behaviour)

配置可能性无穷无尽,具体取决于您的特定用例。例如,您可以仅配置根记录器,并确保所有记录器都传播(无论如何,它们默认都会这样做)。如果他们想要记录任何内容,这还将打印来自您正在使用的第三方库的所有消息。或者,您可以引入其他记录器aaaBot.common,除了将子记录器的记录传播并发布到控制台等之外,还将这些记录器记录到文件中。

关于您的评论:

  

开发库似乎没问题,但我的项目是应用程序。

没关系;它仅取决于__name__变量的值(导入模块而不是执行它)。例如,在logging.getLogger(__name__)中使用aaaBot/main.py不会创建名称为aaaBot.main且父项为aaaBot的记录器。而是将创建一个名为__main__的记录器。

库和应用程序之间的日志记录设置的区别仅在于记录器配置。如果需要日志记录,应用程序将始终显式配置日志记录。一个库根本没有配置任何日志记录,除了NullHandler到该库的根记录器之外。例如,如果aaaBot是一个库,则aaaBot/__init__.py中的日志记录设置可能如下所示:

import logging
LIB_LOGGER = logging.getLogger('aaaBot')
if not LIB_LOGGER.handlers:
    LIB_LOGGER.addHandler(logging.NullHandler())