日志记录

Python 程序员经常会在他们的代码中使用 print() 作为一种快速便捷的调试工具。使用日志记录框架只需要稍微多一点的努力,但它更加优雅和灵活。除了用于调试之外,日志记录还可以为您提供更多关于应用程序状态和健康状况的信息,并且这些信息结构更合理。

概述

Django 使用并扩展了 Python 内置的 logging 模块来执行系统日志记录。此模块在 Python 自身文档中进行了详细讨论;本节提供了一个快速概述。

参与者

Python 日志记录配置由四个部分组成

记录器

记录器是日志记录系统的入口点。每个记录器都是一个命名的容器,可以将消息写入其中进行处理。

记录器被配置为具有一个日志级别。此日志级别描述了记录器将处理的消息的严重性。Python 定义了以下日志级别

  • DEBUG: 用于调试目的的低级系统信息

  • INFO: 一般系统信息

  • WARNING: 描述已发生的小问题的的信息。

  • ERROR: 描述已发生的大问题的的信息。

  • CRITICAL: 描述已发生的严重问题的的信息。

写入记录器的每条消息都是一个日志记录。每个日志记录还具有一个日志级别,指示该特定消息的严重性。日志记录还可以包含描述正在记录的事件的有用元数据。这可能包括诸如堆栈跟踪或错误代码之类的详细信息。

当消息传递给记录器时,会将消息的日志级别与记录器的日志级别进行比较。如果消息的日志级别等于或超过记录器本身的日志级别,则该消息将进行进一步处理。如果不满足条件,则该消息将被忽略。

一旦记录器确定需要处理某条消息,它就会将其传递给处理器

处理器

处理器是确定记录器中每条消息将发生什么的引擎。它描述了一种特定的日志记录行为,例如将消息写入屏幕、文件或网络套接字。

与记录器一样,处理器也具有日志级别。如果日志记录的日志级别不等于或超过处理器的级别,则处理器将忽略该消息。

一个记录器可以有多个处理器,并且每个处理器可以具有不同的日志级别。这样,可以根据消息的重要性提供不同的通知形式。例如,您可以安装一个处理器,将ERRORCRITICAL 消息转发到寻呼服务,而第二个处理器将所有消息(包括 ERRORCRITICAL 消息)记录到文件中以供以后分析。

过滤器

过滤器用于提供对哪些日志记录从记录器传递到处理器的额外控制。

默认情况下,任何满足日志级别要求的日志消息都将被处理。但是,通过安装过滤器,您可以对日志记录过程设置其他条件。例如,您可以安装一个过滤器,只允许发出来自特定源的 ERROR 消息。

过滤器也可用于在发出日志记录之前修改日志记录。例如,您可以编写一个过滤器,如果满足特定条件集,则将 ERROR 日志记录降级为 WARNING 记录。

过滤器可以安装在记录器或处理器上;多个过滤器可以串联使用以执行多个过滤操作。

格式化器

最终,日志记录需要呈现为文本。格式化器描述了该文本的确切格式。格式化器通常由包含 日志记录属性 的 Python 格式化字符串组成;但是,您还可以编写自定义格式化器来实现特定的格式化行为。

安全隐患

日志记录系统处理可能敏感的信息。例如,日志记录可能包含有关 Web 请求或堆栈跟踪的信息,而您在自己的记录器中收集的一些数据也可能存在安全隐患。您需要确保知道

  • 收集了哪些信息

  • 它随后将存储在哪里

  • 它将如何传输

  • 谁可能访问它。

为了帮助控制敏感信息的收集,您可以明确指定某些敏感信息应从错误报告中过滤掉 - 阅读更多关于如何 过滤错误报告

AdminEmailHandler

内置的 AdminEmailHandler 值得在安全上下文中提及。如果其 include_html 选项已启用,则它发送的电子邮件消息将包含完整的回溯,包括堆栈每一级的局部变量的名称和值,以及 Django 设置的值(换句话说,与 DEBUGTrue 时在网页中公开的详细程度相同)。

通常不建议通过电子邮件发送此类可能敏感的信息。请考虑改用众多第三方服务之一,将详细日志发送到这些服务以获得多方面的优势 - 包括完整回溯的丰富信息、对谁被通知以及谁有权访问信息的清晰管理等等。

配置日志记录

Python 的日志记录库提供了多种配置日志记录的技术,从编程接口到配置文件。默认情况下,Django 使用 dictConfig 格式

为了配置日志记录,您可以使用 LOGGING 定义日志记录设置的字典。这些设置描述了您在日志记录设置中所需的记录器、处理器、过滤器和格式化器,以及您希望这些组件具有的日志级别和其他属性。

默认情况下,LOGGING 设置将与 Django 的默认日志记录配置 使用以下方案合并。

如果 LOGGING 的 dictConfig 中的 disable_existing_loggers 键设置为 True(如果缺少此键,则为 dictConfig 的默认值),则默认配置中的所有日志记录器都将被禁用。禁用的日志记录器与删除的日志记录器不同;日志记录器仍然存在,但会静默地丢弃记录到其中的任何内容,甚至不会将条目传播到父日志记录器。因此,在使用 'disable_existing_loggers': True 时要非常小心;这可能不是你想要的。相反,你可以将 disable_existing_loggers 设置为 False 并重新定义一些或所有默认日志记录器;或者你可以将 LOGGING_CONFIG 设置为 None自行处理日志记录配置

日志记录作为 Django 通用 setup() 函数的一部分进行配置。因此,你可以确定日志记录器始终可以在你的项目代码中使用。

示例

有关 dictConfig 格式 的完整文档是有关日志记录配置字典的最佳信息来源。但是,为了让你了解其可能性,这里提供了一些示例。

首先,这是一个小的配置,它允许你将所有日志消息输出到控制台

settings.py
import os

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
}

此配置将父 root 日志记录器配置为将级别为 WARNING 或更高的消息发送到控制台处理程序。通过将级别调整为 INFODEBUG,你可以显示更多消息。这在开发期间可能很有用。

接下来,我们可以添加更细粒度的日志记录。以下是如何使日志记录系统打印更多来自名为 django 的日志记录器的消息的示例

settings.py
import os

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "level": os.getenv("DJANGO_LOG_LEVEL", "INFO"),
            "propagate": False,
        },
    },
}

默认情况下,此配置将来自 django 日志记录器且级别为 INFO 或更高的消息发送到控制台。这与 Django 的默认日志记录配置相同,只是默认配置仅在 DEBUG=True 时显示日志记录。Django 不会记录很多此类 INFO 级别的消息。但是,使用此配置,你还可以设置环境变量 DJANGO_LOG_LEVEL=DEBUG 以查看 Django 的所有调试日志,这些日志非常详细,因为它包含所有数据库查询。

你不必记录到控制台。这是一个将来自名为 django 的日志记录器的所有日志记录写入本地文件的配置

settings.py
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "file": {
            "level": "DEBUG",
            "class": "logging.FileHandler",
            "filename": "/path/to/django/debug.log",
        },
    },
    "loggers": {
        "django": {
            "handlers": ["file"],
            "level": "DEBUG",
            "propagate": True,
        },
    },
}

如果使用此示例,请确保将 'filename' 路径更改为运行 Django 应用程序的用户可写入的位置。

最后,这是一个相当复杂的日志记录设置示例

settings.py
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "verbose": {
            "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
            "style": "{",
        },
        "simple": {
            "format": "{levelname} {message}",
            "style": "{",
        },
    },
    "filters": {
        "special": {
            "()": "project.logging.SpecialFilter",
            "foo": "bar",
        },
        "require_debug_true": {
            "()": "django.utils.log.RequireDebugTrue",
        },
    },
    "handlers": {
        "console": {
            "level": "INFO",
            "filters": ["require_debug_true"],
            "class": "logging.StreamHandler",
            "formatter": "simple",
        },
        "mail_admins": {
            "level": "ERROR",
            "class": "django.utils.log.AdminEmailHandler",
            "filters": ["special"],
        },
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "propagate": True,
        },
        "django.request": {
            "handlers": ["mail_admins"],
            "level": "ERROR",
            "propagate": False,
        },
        "myproject.custom": {
            "handlers": ["console", "mail_admins"],
            "level": "INFO",
            "filters": ["special"],
        },
    },
}

此日志记录配置执行以下操作

  • 将配置标识为“dictConfig 版本 1”格式。目前,这是唯一的 dictConfig 格式版本。

  • 定义两个格式化程序

    • simple,输出日志级别名称(例如,DEBUG)和日志消息。

      format 字符串是描述每个日志记录行上要输出的详细信息的普通 Python 格式化字符串。可以在 Formatter Objects 中找到可以输出的详细信息的完整列表。

    • verbose,输出日志级别名称、日志消息,以及生成日志消息的时间、进程、线程和模块。

  • 定义两个过滤器

    • project.logging.SpecialFilter,使用别名 special。如果此过滤器需要其他参数,则可以作为过滤器配置字典中的其他键提供。在本例中,在实例化 SpecialFilter 时,参数 foo 将被赋予值 bar

    • django.utils.log.RequireDebugTrue,在 DEBUGTrue 时传递记录。

  • 定义两个处理程序

    • console,一个 StreamHandler,它将任何 INFO(或更高)消息打印到 sys.stderr。此处理程序使用 simple 输出格式。

    • mail_admins,一个 AdminEmailHandler,它将任何 ERROR(或更高)消息通过电子邮件发送到站点 ADMINS。此处理程序使用 special 过滤器。

  • 配置三个日志记录器

    • django,它将所有消息传递到 console 处理程序。

    • django.request,它将所有 ERROR 消息传递到 mail_admins 处理程序。此外,此日志记录器被标记为传播消息。这意味着写入 django.request 的日志消息将不会由 django 日志记录器处理。

    • myproject.custom,它将所有级别为 INFO 或更高且通过 special 过滤器的消息传递到两个处理程序——consolemail_admins。这意味着所有 INFO 级别消息(或更高)都将打印到控制台;ERRORCRITICAL 消息也将通过电子邮件输出。

自定义日志记录配置

如果你不想使用 Python 的 dictConfig 格式来配置你的日志记录器,你可以指定你自己的配置方案。

LOGGING_CONFIG 设置定义了将用于配置 Django 日志记录器的可调用对象。默认情况下,它指向 Python 的 logging.config.dictConfig() 函数。但是,如果你想使用不同的配置过程,则可以使用任何其他接受单个参数的可调用对象。配置日志记录时,LOGGING 的内容将作为该参数的值提供。

禁用日志记录配置

如果你根本不想配置日志记录(或者你想使用你自己的方法手动配置日志记录),则可以将 LOGGING_CONFIG 设置为 None。这将禁用 Django 的默认日志记录 的配置过程。

LOGGING_CONFIG 设置为 None 仅表示自动配置过程被禁用,而不是日志记录本身。如果禁用配置过程,Django 仍然会进行日志记录调用,回退到定义的任何默认日志记录行为。

以下是一个禁用 Django 日志记录配置并手动配置日志记录的示例。

settings.py
LOGGING_CONFIG = None

import logging.config

logging.config.dictConfig(...)

请注意,默认配置过程仅在设置完全加载后才会调用一次LOGGING_CONFIG。相比之下,在您的 settings 文件中手动配置日志记录会立即加载您的日志记录配置。因此,您的日志记录配置必须出现在其依赖的任何设置**之后**。

返回顶部