2

我正在使用日志记录并有一个问题。

我知道有简单和高级的日志记录概念。

在简单的日志记录中,我们有logging.info(),等等,而在高级日志记录中,我们有logging.getlogger(some_name).

在简单日志记录中,我们可以使用配置日志路径和消息格式,logging. basicConfig而在高级日志记录的情况下,我们有格式化程序的概念,处理程序分配给通过使用获得的记录器logging.getlogger(some_name).addhandlers..

我们甚至可以使用向根记录器添加多个处理程序logging.getlogger().addhandlers....

因此,高级日志记录的唯一好处是我们可以将记录器名称添加到硬编码值或__name__相应的模块值中。

因此,既然格式化程序和处理程序可以用于简单和高级的日志记录方法,那么简单意味着根记录器和高级意味着模块名称记录器吗?

basicConfig 只能在根记录器上使用,处理程序/格式化程序只能在命名记录器上使用吗?

4

1 回答 1

9

忠告


首先,简单和复杂(或基本和高级)是相对的术语。您可以只使用具有非常复杂的日志记录配置的根记录器,您会称其为简单日志记录,因为您使用的是根记录器吗?不,您不应该将基本和高级等相关术语的语义(含义)与 Python 对象联系起来。语言结构的语义由它们引起的计算或它们产生的效果来表示,这对每个人来说总是相同的。

词典


其次,让我们澄清一些术语。

  • logging是一个 Python module

  • basicConfig&getLogger是模块级函数。

  • debug(), info(),warning()等都是模块级函数和类方法,具体取决于您如何调用它们。如果你这样做logging.debug(msg),你正在调用一个模块级函数,如果你这样做,some_logger.debug(msg)你正在调用一个方法。模块级函数本身也在后台调用 root 方法。

执行流程层次结构


root您导入日志记录机器时,记录器会自动创建,即当您这样做时import logging-root记录器会自动创建,这反过来又使您能够执行直接调用,例如logging.debug()使用根记录器的 。

基本上,模块级函数如下所示:

def debug(msg, *args, **kwargs):
    """
    Log a message with severity 'DEBUG' on the root logger. If the logger has
    no handlers, call basicConfig() to add a console handler with a pre-defined
    format.
    """
    if len(root.handlers) == 0:
        basicConfig()
    root.debug(msg, *args, **kwargs)

记录器按层次结构组织,所有记录器都是记录器的后代root

当您调用getLogger(name)ifname存在时,它将返回 that logger,如果不存在,它将创建 that logger。该getLogger(name)函数是幂等的,这意味着,对于具有相同名称的后续调用,无论您调用多少次,它都只会返回该现有记录器。

该名称可能是一个以句点分隔的分层值,例如foo.bar.baz. 层次列表中更靠下的记录器是列表中更高的记录器的子级。例如,给定一个名称为 的foo记录器,名称为foo.barfoo.bar.baz和的记录器foo.bam都是 的后代foo

创建记录器时,级别设置为NOTSET(当记录器是非根记录器时,这会导致所有消息都委托给父级)。这意味着如果记录器的级别为 NOTSET,则遍历其祖先记录器链,直到找到具有 NOTSET 级别之外的祖先,或者到达根。

在不深入细节的情况下,以下是相关链接:记录器对象模块级函数执行流程

你的问题


在简单日志中,我们可以使用日志来配置日志路径和消息格式。basicConfig 而在高级日志记录的情况下,我们有格式化程序的概念,处理程序分配给通过使用 logging.getlogger(some_name).addhandlers..

不。

正如我们现在所知,basicConfig是一个模块级函数。此函数为您的日志记录系统设置基本配置,应该在其他任何事情之前调用,因为如果您在自己调用之前执行任何类型的日志记录,如果没有为根记录器定义处理程序,则函数如debug(),info()等将自动调用basicConfig(). 这个函数也是幂等的,也就是说你调用一次,你可以调用十亿次,没有任何效果。但是这个调用将决定你的日志记录将如何为所有记录器工作,而不仅仅是根(因为所有记录器都通过层次结构连接)并将消息从一个记录器传递到另一个记录器,除非你为后代记录器指定显式配置。

路径是您希望记录日志消息的位置,它是通过处理程序设置的,它可以是控制台、文件、电子邮件,等等……请参阅此处的完整列表

格式是您希望消息显示的方式,您希望它们包含什么样的信息,这是通过formatters完成的,您可以在其中提供所需的日志记录属性。这些属性决定了日志记录知道哪些信息。

但这一切都是协同工作的。Handlers附于loggersformatters附于handlers您可以通过basicConfigdictConfigfileConfig为整​​个应用程序设置一次,或者您可以单独设置它们,每个logger.

因此,高级日志记录的唯一好处是我们可以将记录器名称添加到硬编码值或相应模块值的名称中。

不。

更复杂的日志记录意味着您可以将应用程序拆分为模块并loggers为每个模块单独设置,并拥有一个非常完善的消息系统,其中应用程序的每个部分记录不同的内容(您希望敏感部分记录非常具体的信息,也许通过电子邮件快速发送它们或将它们记录到文件中),而您希望琐碎的部分轻松记录并通过控制台打印它们。

basicConfig 只能在根记录器上使用,处理程序/格式化程序只能在命名记录器上使用吗?

basicConfigroot除非另有说明,否则将为所有记录器将使用的记录器设置配置。

例子


import logging

root = logging.getLogger()
print(root.handlers)  # no handlers at this point
logging.warning('hello')  # calls basicConfig
print(root.handlers)  # has handler now

# create file handler
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.ERROR)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)

# add the handlers to the logger
root.addHandler(fh)

print(root.handlers)  # now has 2 handlers
root.warning('whats good')  # will only show to console
root.error('whats good')  # will show to console and file

random_logger = logging.getLogger('bogus')  # another logger, descendant from root
random_logger.warning('im random')  # will use root handlers, meaning it will show to console
random_logger.error('im random error')  # same as above, both console and file

# and you can ofc add handlers and what not differently to this non root logger
于 2019-09-05T06:34:48.290 回答