Ведение журнала

Программисты Python часто используют print() в своем коде как быстрый и удобный инструмент отладки. Использование структуры протоколирования требует лишь немного больше усилий, но оно гораздо более элегантно и гибко. Помимо полезности для отладки, протоколирование также может предоставить вам больше - и лучше структурированной - информации о состоянии и работоспособности вашего приложения.

Быстрый обзор

Django uses and extends Python’s builtin logging module to perform system logging. This module is discussed in detail in Python’s own documentation; this section provides a quick overview.

Состав игроков

Конфигурация ведения журнала Python состоит из четырех частей:

Логеры

A logger is the entry point into the logging system. Each logger is a named bucket to which messages can be written for processing.

Логер настроен на уровень журнала. Этот уровень журнала описывает серьезность сообщений, обрабатываемых логером. Python определяет следующие уровни журнала:

  • DEBUG: системная информация низкого уровня для отладки.
  • INFO: Общая информация о системе
  • WARNING: Информация, описывающая возникшую незначительную проблему.
  • ERROR: Информация, описывающая возникшую серьезную проблему.
  • CRITICAL: Информация, описывающая возникшую критическую проблему.

Каждое сообщение, которое записывается в регистратор, является записью журнала. Каждая запись журнала также имеет уровень журнала, указывающий на серьезность данного конкретного сообщения. Запись журнала может также содержать полезные метаданные, описывающие регистрируемое событие. Это могут быть такие детали, как трассировка стека или код ошибки.

Когда сообщение поступает в регистратор, уровень журнала сообщения сравнивается с уровнем журнала регистратора. Если уровень журнала сообщения соответствует уровню журнала самого регистратора или превышает его, сообщение будет обработано. Если нет, то сообщение будет проигнорировано.

Как только регистратор определил, что сообщение должно быть обработано, оно передается в Handler.

Обработчики

The handler is the engine that determines what happens to each message in a logger. It describes a particular logging behavior, such as writing a message to the screen, to a file, or to a network socket.

Как и регистраторы, обработчики также имеют уровень журнала. Если уровень записи журнала не соответствует уровню обработчика или превышает его, обработчик проигнорирует сообщение.

У регистратора может быть несколько обработчиков, и каждый обработчик может иметь свой уровень регистрации. Таким образом, можно обеспечить различные формы уведомления в зависимости от важности сообщения. Например, можно установить один обработчик, который пересылает сообщения ERROR и CRITICAL в службу подкачки, а второй обработчик записывает все сообщения (включая ERROR и CRITICAL) в файл для последующего анализа.

Фильтры

A filter is used to provide additional control over which log records are passed from logger to handler.

По умолчанию обрабатывается любое сообщение журнала, отвечающее требованиям уровня журнала. Однако, установив фильтр, вы можете задать дополнительные критерии для процесса протоколирования. Например, вы можете установить фильтр, который позволяет выдавать только сообщения ERROR из определенного источника.

Фильтры также можно использовать для изменения записи журнала перед ее выдачей. Например, вы можете написать фильтр, который понижает уровень ERROR записей журнала до WARNING записей, если выполняется определенный набор критериев.

Фильтры можно устанавливать на регистраторы или на обработчики; несколько фильтров можно использовать в цепочке для выполнения нескольких действий фильтрации.

Форматировщики

Ultimately, a log record needs to be rendered as text. Formatters describe the exact format of that text. A formatter usually consists of a Python formatting string containing LogRecord attributes; however, you can also write custom formatters to implement specific formatting behavior.

Последствия для безопасности

T

  • Система протоколирования обрабатывает потенциально конфиденциальную информацию. Например, запись журнала может содержать информацию о веб-запросе или трассировке стека, а некоторые данные, которые вы собираете в собственных регистраторах, также могут иметь отношение к безопасности. Вы должны быть уверены в том, что знаете:
  • где он будет впоследствии храниться
  • как она будет передана
  • которые могут иметь к нему доступ.

Чтобы помочь контролировать сбор конфиденциальной информации, вы можете явно указать определенную конфиденциальную информацию, которая будет отфильтрована из отчетов об ошибках - читайте подробнее о том, как это сделать filter error reports.

AdminEmailHandler

Встроенный AdminEmailHandler заслуживает упоминания в контексте безопасности. Если его опция include_html включена, то отправляемое им сообщение электронной почты будет содержать полный трассировочный откат, с именами и значениями локальных переменных на каждом уровне стека, плюс значения ваших настроек Django (другими словами, тот же уровень детализации, который раскрывается на веб-странице, когда DEBUG становится True).

Обычно считается, что отправлять такую потенциально важную информацию по электронной почте не стоит. Вместо этого воспользуйтесь одним из многочисленных сторонних сервисов, на которые можно отправлять подробные журналы, чтобы получить лучшее из нескольких миров - богатую информацию о полном отслеживании, четкое управление тем, кто уведомлен и имеет доступ к информации, и так далее.

Настройка протоколирования

Библиотека протоколирования Python предоставляет несколько способов настройки протоколирования, начиная от программного интерфейса и заканчивая конфигурационными файлами. По умолчанию Django использует dictConfig format.

In order to configure logging, you use LOGGING to define a dictionary of logging settings. These settings describe the loggers, handlers, filters and formatters that you want in your logging setup, and the log levels and other properties that you want those components to have.

По умолчанию установка LOGGING объединяется с Django’s default logging configuration по следующей схеме.

Если ключ disable_existing_loggers в директ-конфиге LOGGING установлен в True (что является dictConfig по умолчанию, если ключ отсутствует), то все регистраторы из конфигурации по умолчанию будут отключены. Отключенные регистраторы - это не то же самое, что удаленные; регистратор будет по-прежнему существовать, но будет молча отбрасывать все, что записывается в него, даже не передавая записи в родительский регистратор. Таким образом, вы должны быть очень осторожны, используя 'disable_existing_loggers': True; вероятно, это не то, что вам нужно. Вместо этого вы можете установить disable_existing_loggers в False и переопределить некоторые или все логгеры по умолчанию; или вы можете установить LOGGING_CONFIG в None и handle logging config yourself.

Логирование настраивается как часть общей функции Django setup(). Поэтому вы можете быть уверены, что логгеры всегда готовы к использованию в коде вашего проекта.

Примеры:

Полная документация по dictConfig format является лучшим источником информации о словарях конфигурации журнала. Однако, чтобы дать вам представление о том, что возможно, приведем несколько примеров.

Для начала, вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:

settings.py
import os

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

Это настраивает родительский регистратор root на отправку сообщений с уровнем WARNING и выше в обработчик консоли. Изменив уровень до INFO или DEBUG, можно вывести больше сообщений. Это может быть полезно во время разработки.

Далее мы можем добавить более тонкое протоколирование. Вот пример того, как заставить систему протоколирования выводить больше сообщений только из именованного регистратора 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. Если для данного фильтра требуются дополнительные аргументы, они могут быть предоставлены как дополнительные ключи в словаре конфигурации фильтра. В этом случае аргументу foo будет присвоено значение bar при инстанцировании SpecialFilter.
    • django.utils.log.RequireDebugTrue, который передает записи, когда DEBUG становится True.
  • Определяет два обработчика:

    • console, обработчик StreamHandler, который печатает любое сообщение INFO (или выше) в sys.stderr. Этот обработчик использует формат вывода simple.
    • mail_admins, an AdminEmailHandler, which emails any ERROR (or higher) message to the site ADMINS. This handler uses the special filter.
  • Настраивает три регистратора:

    • django, который передает все сообщения обработчику console.
    • django.request, который передает все сообщения ERROR обработчику mail_admins. Кроме того, этот логгер помечен как не распространяющий сообщения. Это означает, что сообщения журнала, записанные в django.request, не будут обработаны регистратором django.
    • myproject.custom, который передает все сообщения уровня INFO или выше, которые также проходят фильтр special, двум обработчикам - console и mail_admins. Это означает, что все сообщения уровня INFO (или выше) будут выведены на консоль; ERROR и CRITICAL сообщения также будут выведены по электронной почте.

Пользовательская конфигурация протоколирования

Если вы не хотите использовать формат Python dictConfig для настройки регистратора, вы можете задать свою собственную схему конфигурации.

Параметр LOGGING_CONFIG определяет вызываемую функцию, которая будет использоваться для настройки логгеров Django. По умолчанию он указывает на функцию Python logging.config.dictConfig(). Однако, если вы хотите использовать другой процесс конфигурации, вы можете использовать любую другую вызываемую функцию, принимающую один аргумент. Содержимое LOGGING будет предоставлено в качестве значения этого аргумента при настройке логирования.

Отключение конфигурации ведения журнала

If you don’t want to configure logging at all (or you want to manually configure logging using your own approach), you can set LOGGING_CONFIG to None. This will disable the configuration process for Django’s default logging.

Установка LOGGING_CONFIG в None означает только отключение процесса автоматической настройки, но не самого протоколирования. Если вы отключите процесс конфигурирования, Django все равно будет выполнять вызовы логирования, возвращаясь к тому поведению логирования, которое определено по умолчанию.

Вот пример, который отключает конфигурацию логирования Django, а затем вручную настраивает логирование:

settings.py
LOGGING_CONFIG = None

import logging.config
logging.config.dictConfig(...)

Обратите внимание, что процесс настройки по умолчанию вызывает LOGGING_CONFIG только после того, как настройки полностью загружены. В отличие от этого, ручная настройка логирования в вашем файле настроек загрузит ваш конфиг логирования немедленно. Таким образом, конфигурация логирования должна появиться после всех настроек, от которых она зависит.

Вернуться на верх