Ведение журнала¶
Программисты Python часто используют print()
в своем коде как быстрый и удобный инструмент отладки. Использование фреймворка протоколирования требует лишь немного больше усилий, но оно гораздо более элегантно и гибко. Помимо полезности для отладки, протоколирование также может предоставить вам больше - и лучше структурированной - информации о состоянии и работоспособности вашего приложения.
Быстрый обзор¶
Django использует и расширяет встроенный модуль Python logging
для ведения системного журнала. Этот модуль подробно рассматривается в собственной документации Python; в данном разделе представлен краткий обзор.
Состав игроков¶
Конфигурация ведения журнала Python состоит из четырех частей:
Логеры¶
Журнал logger - это точка входа в систему протоколирования. Каждый логгер представляет собой именованное ведро, в которое можно записывать сообщения для обработки.
Логер настроен на уровень журнала. Этот уровень журнала описывает серьезность сообщений, обрабатываемых логером. Python определяет следующие уровни журнала:
DEBUG
: системная информация низкого уровня для отладкиINFO
: Общая информация о системеWARNING
: Информация, описывающая возникшую незначительную проблему.ERROR
: Информация, описывающая возникшую серьезную проблему.CRITICAL
: Информация, описывающая возникшую критическую проблему.
Каждое сообщение, которое записывается в регистратор, является записью журнала. Каждая запись журнала также имеет уровень журнала, указывающий на серьезность данного конкретного сообщения. Запись журнала может также содержать полезные метаданные, описывающие регистрируемое событие. Это могут быть такие детали, как трассировка стека или код ошибки.
Когда сообщение поступает в регистратор, уровень журнала сообщения сравнивается с уровнем журнала регистратора. Если уровень журнала сообщения соответствует уровню журнала самого регистратора или превышает его, сообщение будет обработано. Если нет, то сообщение будет проигнорировано.
Как только регистратор определил, что сообщение должно быть обработано, оно передается в Handler.
Обработчики¶
Обработчик* - это механизм, определяющий, что происходит с каждым сообщением в регистраторе. Он описывает определенное поведение регистратора, например, запись сообщения на экран, в файл или в сетевой сокет.
Как и регистраторы, обработчики также имеют уровень журнала. Если уровень записи журнала не соответствует уровню обработчика или превышает его, обработчик проигнорирует сообщение.
У регистратора может быть несколько обработчиков, и каждый обработчик может иметь свой уровень регистрации. Таким образом, можно обеспечить различные формы уведомления в зависимости от важности сообщения. Например, можно установить один обработчик, который пересылает сообщения ERROR
и CRITICAL
в службу подкачки, а второй обработчик записывает все сообщения (включая ERROR
и CRITICAL
) в файл для последующего анализа.
Фильтры¶
Для обеспечения дополнительного контроля над тем, какие записи журнала передаются от регистратора к обработчику, используется фильтр.
По умолчанию обрабатывается любое сообщение журнала, отвечающее требованиям уровня журнала. Однако, установив фильтр, вы можете задать дополнительные критерии для процесса протоколирования. Например, вы можете установить фильтр, который позволяет выдавать только сообщения ERROR
из определенного источника.
Фильтры также можно использовать для изменения записи журнала перед ее выдачей. Например, вы можете написать фильтр, который понижает уровень ERROR
записей журнала до WARNING
записей, если выполняется определенный набор критериев.
Фильтры можно устанавливать на регистраторы или на обработчики; несколько фильтров можно использовать в цепочке для выполнения нескольких действий фильтрации.
Форматировщики¶
В конечном счете, запись журнала должна быть представлена в виде текста. Форматировщики описывают точный формат этого текста. Форматтер обычно состоит из строки форматирования Python, содержащей LogRecord attributes; однако вы также можете написать собственные форматтеры для реализации специфического поведения форматирования.
Последствия для безопасности¶
Система протоколирования обрабатывает потенциально конфиденциальную информацию. Например, запись журнала может содержать информацию о веб-запросе или трассировке стека, а некоторые данные, которые вы собираете в собственных регистраторах, также могут иметь отношение к безопасности. Вы должны быть уверены в том, что знаете:
- какая информация собирается
- где он будет впоследствии храниться
- как она будет передана
- кто может иметь к нему доступ.
Чтобы помочь контролировать сбор конфиденциальной информации, вы можете явно указать определенную конфиденциальную информацию, которая будет отфильтрована из отчетов об ошибках - читайте подробнее о том, как это сделать filter error reports.
AdminEmailHandler
¶
Встроенный AdminEmailHandler
заслуживает упоминания в контексте безопасности. Если его опция include_html
включена, то отправляемое им сообщение электронной почты будет содержать полный трассировочный откат, с именами и значениями локальных переменных на каждом уровне стека, плюс значения ваших настроек Django (другими словами, тот же уровень детализации, который раскрывается на веб-странице, когда DEBUG
становится True
).
Обычно считается, что отправлять такую потенциально важную информацию по электронной почте не стоит. Вместо этого воспользуйтесь одним из многочисленных сторонних сервисов, на которые можно отправлять подробные журналы, чтобы получить лучшее из нескольких миров - богатую информацию о полном отслеживании, четкое управление тем, кто уведомлен и имеет доступ к информации, и так далее.
Настройка протоколирования¶
Библиотека протоколирования Python предоставляет несколько способов настройки протоколирования, начиная от программного интерфейса и заканчивая конфигурационными файлами. По умолчанию Django использует dictConfig format.
Для настройки протоколирования вы используете LOGGING
для определения словаря настроек протоколирования. Эти настройки описывают регистраторы, обработчики, фильтры и форматеры, которые вы хотите использовать в своей настройке протоколирования, а также уровни протоколирования и другие свойства, которыми должны обладать эти компоненты.
По умолчанию установка 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 является лучшим источником информации о словарях конфигурации журнала. Однако, чтобы дать вам представление о том, что возможно, приведем несколько примеров.
Для начала, вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:
import os
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
"root": {
"handlers": ["console"],
"level": "WARNING",
},
}
Это настраивает родительский регистратор root
на отправку сообщений с уровнем WARNING
и выше в обработчик консоли. Изменив уровень до INFO
или DEBUG
, можно вывести больше сообщений. Это может быть полезно во время разработки.
Далее мы можем добавить более тонкое протоколирование. Вот пример того, как заставить систему протоколирования выводить больше сообщений только из именованного регистратора django:
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 именованного логгера в локальный файл:
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.
Наконец, вот пример довольно сложной настройки протоколирования:
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
, обработчикAdminEmailHandler
, который отправляет любое сообщениеERROR
(или выше) на сайтADMINS
. Этот обработчик использует фильтрspecial
.
Настраивает три регистратора:
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
будет предоставлено в качестве значения этого аргумента при настройке логирования.
Отключение конфигурации ведения журнала¶
Если вы не хотите настраивать ведение журнала вообще (или хотите вручную настроить ведение журнала, используя свой собственный подход), вы можете установить LOGGING_CONFIG
на None
. Это отключит процесс конфигурирования для Django’s default logging.
Установка LOGGING_CONFIG
в None
означает только отключение процесса автоматической настройки, но не самого протоколирования. Если вы отключите процесс конфигурирования, Django все равно будет выполнять вызовы логирования, возвращаясь к тому поведению логирования, которое определено по умолчанию.
Вот пример, который отключает конфигурацию логирования Django, а затем вручную настраивает логирование:
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
Обратите внимание, что процесс настройки по умолчанию вызывает LOGGING_CONFIG
только после того, как настройки полностью загружены. В отличие от этого, ручная настройка логирования в вашем файле настроек загрузит ваш конфиг логирования немедленно. Таким образом, конфигурация логирования должна появиться после всех настроек, от которых она зависит.