---
title: "日志"
version: 6.0
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/6.0/topics/logging/
canonical: https://djangodocs.dev/zh-hans/6.0/topics/logging/
---
# 日志

> **See also**
>
> - [如何配置和使用日志](/zh-hans/6.0/howto/logging/#logging-how-to)
> - [Django 日志参考](/zh-hans/6.0/ref/logging/#logging-ref)

Python 程序员通常会在其代码中使用 `print()` 作为一种快速和方便的调试工具。使用日志框架只比这多花一点点工夫，但更加优雅和灵活。除了用于调试之外，日志还可以为您提供有关应用程序状态和健康状况的更多信息，而且这些信息结构更清晰。

## 概况

Django 使用并扩展了 Python 内置的 [`logging`](https://docs.python.org/3/library/logging.html#module-logging) 模块来执行系统日志记录。这个模块在 Python 自己的文档中有详细的讨论；这一部分提供了一个快速概览。

### 日志框架的组成元素

一份 Python logging 配置有下面四个部分组成：

- [Loggers](#topic-logging-parts-loggers)
- [Handlers](#topic-logging-parts-handlers)
- [过滤器](#topic-logging-parts-filters)
- [Formatters](#topic-logging-parts-formatters)

#### Loggers

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

logger 可以配置 *日志级别*。日志级别描述了由该 logger 处理的消息的严重性。Python 定义了下面几种日志级别：

- `DEBUG`：排查故障时使用的低级别系统信息
- `INFO`：一般的系统信息
- `WARNING`：描述系统发生了一些小问题的信息
- `ERROR`：描述系统发生了大问题的信息
- `CRITICAL`：描述系统发生严重问题的信息

每一条写入 logger 的消息都是一条 *日志记录*。每一条日志记录也包含 *日志级别*，代表对应消息的严重程度。日志记录还包含有用的元数据，来描述被记录了日志的事件细节，例如堆栈跟踪或者错误码。

当 logger 处理一条消息时，会将自己的日志级别和这条消息的日志级别做对比。如果消息的日志级别匹配或者高于 logger 的日志级别，它就会被进一步处理。否则这条消息就会被忽略掉。

当 logger 确定了一条消息需要处理之后，会把它传给 *Handler*。

#### Handlers

*handler* 是确定 logger 中每个消息发生什么的引擎。它描述了特定的日志行为，例如将消息写入屏幕、写入文件或写入网络套接字。

和 logger 一样，handler 也有日志级别的概念。如果一条日志记录的级别不匹配或者低于 handler 的日志级别，对应的消息会被 handler 忽略。

一个 logger 可以有多个 handler，每一个 handler 可以有不同的日志级别。这样就可以根据消息的重要性不同，来提供不同格式的输出。例如，你可以添加一个 handler 把  `ERROR` 和 `CRITICAL` 消息发到寻呼机，再添加另一个 handler 把所有的消息（包括 `ERROR` 和 `CRITICAL` 消息）保存到文件里以便日后分析。

#### 过滤器

*filter* 用于在从 logger 传递到 handler 的日志记录中提供额外的控制。

默认情况下，只要级别匹配，任何日志消息都会被处理。不过，也可以通过添加 filter 来给日志处理的过程增加额外条件。例如，可以添加一个 filter 只允许某个特定来源的 `ERROR` 消息输出。

Filter 还被用来在日志输出之前对日志记录做修改。例如，可以写一个 filter，当满足一定条件时，把日志记录从 `ERROR` 降到 `WARNING` 级别。

Filter 在 logger 和 handler 中都可以添加；多个 filter 可以链接起来使用，来做多重过滤操作。

#### Formatters

最终，日志记录需要呈现为文本。*Formatter* 描述了文本的精确格式。Formatter 通常由一个包含 [LogRecord 属性](https://docs.python.org/3/library/logging.html#logrecord-attributes) 的 Python 格式化字符串组成；但是，您也可以编写自定义的 Formatter 来实现特定的格式化行为。

## 安全性考虑

日志记录系统处理可能包含敏感信息的数据。例如，日志记录可能包含有关 Web 请求或堆栈跟踪的信息，而您自己的日志记录中收集的一些数据也可能具有安全性影响。您需要确保自己明白以下事项：

- 会收集什么信息
- 日志记录将随后存储在何处。
- 它将如何传输。
- 谁可能访问它

为了帮助控制敏感信息的收集，您可以明确指定某些敏感信息在错误报告中被过滤掉，详细了解如何 [过滤错误报告](/zh-hans/6.0/howto/error-reporting/#filtering-error-reports)。

### `AdminEmailHandler`

在安全性的背景下，应该提到内置的 [`AdminEmailHandler`](/zh-hans/6.0/ref/logging/#django.utils.log.AdminEmailHandler)。如果启用了它的 `include_html` 选项，它发送的电子邮件消息将包含完整的回溯信息，以及堆栈的每个级别的本地变量的名称和值，以及您的 Django 设置的值（换句话说，与在 [`DEBUG`](/zh-hans/6.0/ref/settings/#std-setting-DEBUG) 为 `True` 时在 Web 页面中显示的详细程度相同）。

通常不建议通过电子邮件发送此类可能包含敏感信息的信息。相反，考虑使用其中许多第三方服务之一，将详细日志发送到这些服务，以获得多个优势：完整回溯信息的丰富信息、清晰的通知和访问信息的管理等等。这样可以更好地处理敏感信息。

## 日志模块的配置

Python 的日志库提供了一些配置方法，可以使用编程接口或者配置文件。Django默认使用 [dictConfig format](https://docs.python.org/3/library/logging.config.html#logging-config-dictschema)。

要配置日志记录，您使用 [`LOGGING`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING) 来定义一个日志设置字典。这些设置描述了您希望在日志设置中使用的记录器、处理程序、过滤器和格式化程序，以及您希望这些组件具有的日志级别和其他属性。

默认情况下 [`LOGGING`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING) 配置和 [Django 默认日志配置](/zh-hans/6.0/ref/logging/#default-logging-configuration)  按照下面的方式合并在一起：

如果 [`LOGGING`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING) dictConfig 中的 `disable_existing_loggers` 键被设置为 `True` （如果该键缺失，则为 `dictConfig` 默认值），则默认配置中的所有记录器都将被禁用。禁用的记录器与删除的记录器不同；记录器仍将存在，但会默默地丢弃任何记录到它的内容，甚至不会将条目传播到父记录仪。因此，你应该非常小心地使用 `'disable_existing_loggers': True`；这可能不是你想要的。相反，你可以将 `disable_existing_loggers` 设置为 `False`，然后重新定义一些或所有的默认日志记录器；或者你可以将 [`LOGGING_CONFIG`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING_CONFIG) 设置为 `None`，然后 [自己处理日志配置](#disabling-logging-configuration)。

logging 被配置成了 Django  `setup()` 函数的一部分。因此，你可以确定的是，logger 一直都可以在项目代码里使用。

### 示例

[dictConfig format](https://docs.python.org/3/library/logging.config.html#logging-config-dictschema) 文档是获取日志配置细节的最好资料。不过，为了让你知道能做什么，下面有几个例子。

首先，这里有一个小配置，可以让你把所有的日志信息输出到控制台。

*`settings.py`*

```python
import os

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

这将配置父 `root` 记录器，以向控制台处理程序发送 `WARNING` 级别及以上的消息。通过将级别调整为 `INFO` 或 `DEBUG`，可以显示更多的消息。这在开发过程中可能很有用。

接下来我们可以添加更多细粒度的日志记录。下面是一个例子，说明如何让日志系统只从名为 logger 的 [django](/zh-hans/6.0/ref/logging/#django-logger) 中打印更多的消息。

*`settings.py`*

```python
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](/zh-hans/6.0/ref/logging/#django-logger) 命名的记录器的日志记录写入本地文件。

*`settings.py`*

```python
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`*

```python
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](https://docs.python.org/3/library/logging.html#formatter-objects) 中找到。
  - `verbose`，输出日志级别名称、日志信息，以及生成日志信息的时间、进程、线程和模块。
- 定义两个过滤器：

  - `project.logging.SpecialFilter`，使用别名 `special`。如果这个过滤器需要额外的参数，它们可以作为过滤器配置字典中的附加键提供。在这种情况下，当实例化 `SpecialFilter` 时，参数 `foo` 将被赋予一个 `bar` 的值。
  - `django.utils.log.RequireDebugTrue`，当 [`DEBUG`](/zh-hans/6.0/ref/settings/#std-setting-DEBUG) 为 `True` 时，传递记录。
- 定义两个处理程序：

  - `console`，一个 [`StreamHandler`](https://docs.python.org/3/library/logging.handlers.html#logging.StreamHandler)，它将任何 `INFO` （或更高）消息打印到 `sys.stderr`。该处理程序使用 `simple` 输出格式。
  - `mail_admins` 是一个 [`AdminEmailHandler`](/zh-hans/6.0/ref/logging/#django.utils.log.AdminEmailHandler)，它会将任何 `ERROR` （或更高级别）的消息通过电子邮件发送给站点的 [`ADMINS`](/zh-hans/6.0/ref/settings/#std-setting-ADMINS)。这个处理程序使用了 `special` 过滤器。
- 配置三个记录器。

  - `django`，将所有信息传递给 `console` 处理程序。
  - `django.request`，它将所有 `ERROR` 消息传递给 `mail_admins` 处理程序。此外，这个记录器被标记为 *不* 传播消息。这意味着写给 `django.request` 的日志信息不会被 `django` 日志处理程序处理。
  - `myproject.custom`，它将所有 `INFO` 或更高等级的消息传递给两个处理程序——`console` 和 `mail_admins`。这意味着所有 `INFO` 级别（或更高）的消息将被打印到控制台；`ERROR` 和 `CRITICAL` 消息也将通过电子邮件输出。

### 自定义日志记录配置

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

The [`LOGGING_CONFIG`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING_CONFIG) setting defines the callable that will
be used to configure Django's loggers. By default, it points at
Python's [`logging.config.dictConfig()`](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig) function. However, if you want to
use a different configuration process, you can use any other callable
that takes a single argument. The contents of [`LOGGING`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING) will
be provided as the value of that argument when logging is configured.

### 禁用日志记录配置

如果你根本不想配置日志记录（或者你想用自己的方法手动配置日志记录），你可以将 [`LOGGING_CONFIG`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING_CONFIG) 设置为 `None`。这将禁用 [Django 的默认日志记录](/zh-hans/6.0/ref/logging/#default-logging-configuration) 的配置过程。

将 [`LOGGING_CONFIG`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING_CONFIG) 设置为 `None` 只是意味着自动配置过程被禁用，而不是日志本身。如果你禁用了配置过程，Django 仍然会进行日志调用，回到默认的日志行为。

下面是一个禁用 Django 的日志配置，然后手动配置日志的例子。

*`settings.py`*

```python
LOGGING_CONFIG = None

import logging.config

logging.config.dictConfig(...)
```

请注意，默认的配置过程只有在设置完全加载后才会调用 [`LOGGING_CONFIG`](/zh-hans/6.0/ref/settings/#std-setting-LOGGING_CONFIG)。相反，在设置文件中手动配置日志记录将立即加载你的日志记录配置。因此，你的日志配置必须出现在它所依赖的任何设置之后。
