如何配置和使用日志

Django provides a working default logging configuration that is readily extended.

发起基本的 logging 调用

要从代码中发送日志消息,你需要在代码中添加日志调用。

Don't be tempted to use logging calls in settings.py.

The way that Django logging is configured as part of the setup() function means that logging calls placed in settings.py may not work as expected, because logging will not be set up at that point. To explore logging, use a view function as suggested in the example below.

First, import the Python logging library, and then obtain a logger instance with logging.getLogger(). Provide the getLogger() method with a name to identify it and the records it emits. A good option is to use __name__ (see 使用命名空间日志记录器 below for more on this) which will provide the name of the current Python module as a dotted path:

import logging

logger = logging.getLogger(__name__)

在模块级别执行此声明是一个好的约定。

然后在函数中,例如在视图中,向日志记录器发送一条记录:

def some_view(request):
    ...
    if some_risky_state:
        logger.warning("Platform is running at risk")

When this code is executed, a LogRecord containing that message will be sent to the logger. If you're using Django's default logging configuration, the message will appear in the console.

The WARNING level used in the example above is one of several logging severity levels: DEBUG, INFO, WARNING, ERROR, CRITICAL. So, another example might be:

logger.critical("Payment system is not responding")

重要

Records with a level lower than WARNING will not appear in the console by default. Changing this behavior requires additional configuration.

自定义日志配置

虽然 Django 的日志配置可以直接使用,但您可以通过一些额外的配置来精确控制日志发送到各种目标的方式,如日志文件、外部服务、电子邮件等。

你可以配置:

  • 使用日志记录器映射来确定将哪些记录发送到哪些处理程序。
  • 使用处理程序来确定它们接收到的记录的处理方式
  • 使用过滤器来对记录的传递进行额外控制,甚至可以对记录进行原地修改
  • formatters, to convert LogRecord objects to a string or other form for consumption by human beings or another system

There are various ways of configuring logging. In Django, the LOGGING setting is most commonly used. The setting uses the dictConfig format, and extends the default logging configuration.

See 日志模块的配置 for an explanation of how your custom settings are merged with Django's defaults.

See the Python logging documentation for details of other ways of configuring logging. For the sake of simplicity, this documentation will only consider configuration via the LOGGING setting.

基础日志配置

在配置日志时,以下操作是有意义的

创建一个 LOGGING 目录

在你的 settings.py:: 中

LOGGING = {
    "version": 1,  # the dictConfig format version
    "disable_existing_loggers": False,  # retain the default loggers
}

It nearly always makes sense to retain and extend the default logging configuration by setting disable_existing_loggers to False.

配置一个处理程序

This example configures a single handler named file, that uses Python's FileHandler to save logs of level DEBUG and higher to the file general.log (at the project root):

LOGGING = {
    # ...
    "handlers": {
        "file": {
            "class": "logging.FileHandler",
            "filename": "general.log",
        },
    },
}

Different handler classes take different configuration options. For more information on available handler classes, see the AdminEmailHandler provided by Django and the various handler classes provided by Python.

日志级别也可以在处理程序上进行设置(默认情况下,它们接受所有级别的日志消息)。使用上面的示例,添加以下内容:

{
    "class": "logging.FileHandler",
    "filename": "general.log",
    "level": "DEBUG",
}

would define a handler configuration that only accepts records of level DEBUG and higher.

配置一个日志记录器映射

要将记录发送到该处理程序,请配置一个日志记录器映射,例如:

LOGGING = {
    # ...
    "loggers": {
        "": {
            "level": "DEBUG",
            "handlers": ["file"],
        },
    },
}

The mapping's name determines which log records it will process. This configuration ('') is unnamed. That means that it will process records from all loggers (see 使用命名空间日志记录器 below on how to use the mapping name to determine the loggers for which it will process records).

It will forward messages of levels DEBUG and higher to the handler named file.

请注意,一个日志记录器可以将消息转发给多个处理程序,因此日志记录器和处理程序之间的关系是多对多的。

如果您执行以下操作:

logger.debug("Attempting to connect to API")

in your code, you will find that message in the file general.log in the root of the project.

配置格式化器

By default, the final log output contains the message part of each log record. Use a formatter if you want to include additional data. First name and define your formatters - this example defines formatters named verbose and simple:

LOGGING = {
    # ...
    "formatters": {
        "verbose": {
            "format": "{name} {levelname} {asctime} {module} {process:d} {thread:d} {message}",
            "style": "{",
        },
        "simple": {
            "format": "{levelname} {message}",
            "style": "{",
        },
    },
}

The style keyword allows you to specify { for str.format() or $ for string.Template formatting; the default is $.

See LogRecord attributes for the LogRecord attributes you can include.

To apply a formatter to a handler, add a formatter entry to the handler's dictionary referring to the formatter by name, for example:

"handlers": {
    "file": {
        "class": "logging.FileHandler",
        "filename": "general.log",
        "formatter": "verbose",
    },
}

使用命名空间日志记录器

The unnamed logging configuration '' captures logs from any Python application. A named logging configuration will capture logs only from loggers with matching names.

The namespace of a logger instance is defined using getLogger(). For example in views.py of my_app:

logger = logging.getLogger(__name__)

will create a logger in the my_app.views namespace. __name__ allows you to organize log messages according to their provenance within your project's applications automatically. It also ensures that you will not experience name collisions.

A logger mapping named my_app.views will capture records from this logger:

LOGGING = {
    # ...
    "loggers": {
        "my_app.views": {...},
    },
}

A logger mapping named my_app will be more permissive, capturing records from loggers anywhere within the my_app namespace (including my_app.views, my_app.utils, and so on):

LOGGING = {
    # ...
    "loggers": {
        "my_app": {...},
    },
}

您还可以明确定义日志记录器的命名空间:

logger = logging.getLogger("project.payment")

并相应地设置日志记录器映射。

使用日志记录器层次结构和传播

Logger naming is hierarchical. my_app is the parent of my_app.views, which is the parent of my_app.views.private. Unless specified otherwise, logger mappings will propagate the records they process to their parents - a record from a logger in the my_app.views.private namespace will be handled by a mapping for both my_app and my_app.views.

为了管理这种行为,请在您定义的映射中设置传播键:

LOGGING = {
    # ...
    "loggers": {
        "my_app": {
            # ...
        },
        "my_app.views": {
            # ...
        },
        "my_app.views.private": {
            # ...
            "propagate": False,
        },
    },
}

propagate defaults to True. In this example, the logs from my_app.views.private will not be handled by the parent, but logs from my_app.views will.

配置响应式日志记录

当日志包含尽可能多的信息,而不是您不需要的信息时,日志是最有用的——需要多少取决于您正在做的事情。在调试时,您需要一定程度的信息,如果您不得不在生产环境中处理这些信息,那么这些信息将是多余的,而且毫无用处。

您可以配置日志记录,以在需要时为您提供所需的详细程度。与手动更改配置以实现此目的不同,更好的方式是根据环境自动应用配置。

For example, you could set an environment variable DJANGO_LOG_LEVEL appropriately in your development and staging environments, and make use of it in a logger mapping thus:

"level": os.getenv("DJANGO_LOG_LEVEL", "WARNING")

- so that unless the environment specifies a lower log level, this configuration will only forward records of severity WARNING and above to its handler.

Other options in the configuration (such as the level or formatter option of handlers) can be similarly managed.