Ведение журнала
Программисты на Python часто используют print() в своём коде как быстрый и удобный инструмент отладки. Использование системы ведения журнала требует лишь немного больше усилий, но она гораздо элегантнее и гибче. Помимо отладки, ведение журнала позволяет получать больше информации о состоянии и работоспособности приложения, причём эта информация лучше структурирована.
Обзор
Django использует встроенный модуль Python logging и расширяет его для ведения системного журнала. Этот модуль подробно описан в документации Python; в этом разделе приведён краткий обзор.
Основные компоненты
Конфигурация ведения журнала в Python состоит из четырёх частей:
Регистраторы
Регистратор — это точка входа в систему ведения журнала. Каждый регистратор представляет собой именованный контейнер, в который можно записывать сообщения для последующей обработки.
Регистратору назначается уровень ведения журнала. Этот уровень определяет серьёзность сообщений, которые будет обрабатывать регистратор. В Python определены следующие уровни ведения журнала:
-
DEBUG: системная информация низкого уровня для отладки -
INFO: общая системная информация -
WARNING: информация о возникшей незначительной проблеме. -
ERROR: информация о возникшей серьёзной проблеме. -
CRITICAL: информация о возникшей критической проблеме.
Каждое сообщение, записываемое в регистратор, является записью журнала. Каждая запись журнала также имеет уровень ведения журнала, указывающий на серьёзность конкретного сообщения. Запись журнала может также содержать полезные метаданные, описывающие регистрируемое событие. Например, это может быть трассировка стека или код ошибки.
Когда регистратору передаётся сообщение, его уровень ведения журнала сравнивается с уровнем регистратора. Если уровень сообщения равен уровню регистратора или выше, сообщение проходит дальнейшую обработку. В противном случае сообщение игнорируется.
Определив, что сообщение нужно обработать, регистратор передаёт его обработчику.
Обработчики
Обработчик определяет, что происходит с каждым сообщением регистратора. Он описывает конкретное поведение при ведении журнала, например запись сообщения на экран, в файл или в сетевой сокет.
Как и у регистраторов, у обработчиков есть уровень ведения журнала. Если уровень записи журнала ниже уровня обработчика, обработчик проигнорирует сообщение.
У регистратора может быть несколько обработчиков, у каждого из которых может быть свой уровень ведения журнала. Таким образом, можно предоставлять уведомления разных видов в зависимости от важности сообщения. Например, можно настроить один обработчик для отправки сообщений ERROR и CRITICAL в службу оповещения, а второй — для записи всех сообщений (включая сообщения ERROR и CRITICAL) в файл для последующего анализа.
Фильтры
Фильтр используется для дополнительного управления тем, какие записи журнала передаются от регистратора обработчику.
По умолчанию обрабатываются все сообщения, соответствующие требованиям к уровню ведения журнала. Однако, добавив фильтр, можно задать дополнительные критерии для процесса ведения журнала. Например, можно добавить фильтр, который разрешает выводить только сообщения ERROR из определённого источника.
Фильтры можно также использовать для изменения записи журнала перед её выводом. Например, можно написать фильтр, который понижает уровень записей журнала ERROR до WARNING, если выполнен заданный набор критериев.
Фильтры можно устанавливать на регистраторах или обработчиках; несколько фильтров можно объединить в цепочку, чтобы выполнять последовательную фильтрацию.
Форматтеры
В конечном счёте запись журнала должна быть представлена в виде текста. Форматтеры задают точный формат этого текста. Обычно форматтер представляет собой строку форматирования Python с атрибутами LogRecord; однако можно также написать собственные форматтеры для реализации особого форматирования.
Последствия для безопасности
Система ведения журнала обрабатывает потенциально конфиденциальную информацию. Например, запись журнала может содержать сведения о веб-запросе или трассировку стека, а некоторые данные, собираемые собственными регистраторами, также могут иметь последствия для безопасности. Необходимо точно знать:
- какая информация собирается
- где она будет храниться
- как она будет передаваться
- кто может получить к ней доступ.
Чтобы контролировать сбор конфиденциальной информации, можно явно указать, какие данные нужно исключать из отчётов об ошибках. Подробнее о том, как фильтровать отчёты об ошибках.
AdminEmailHandler
Встроенный AdminEmailHandler заслуживает отдельного упоминания в контексте безопасности. Если включён параметр include_html, отправляемое им электронное письмо будет содержать полную трассировку стека с именами и значениями локальных переменных на каждом уровне стека, а также значения параметров Django (иначе говоря, такой же уровень подробностей, который отображается на веб-странице, когда параметр DEBUG имеет значение True).
Отправлять такую потенциально конфиденциальную информацию по электронной почте обычно не рекомендуется. Вместо этого рассмотрите возможность использования одной из многочисленных сторонних служб, куда можно отправлять подробные журналы. Так вы получите все преимущества: подробную информацию из полной трассировки стека, чёткое управление списком уведомляемых лиц и доступом к информации и многое другое.
Настройка ведения журнала
Библиотека ведения журнала Python предоставляет несколько способов настройки — от программного интерфейса до файлов конфигурации. По умолчанию Django использует формат dictConfig.
Чтобы настроить ведение журнала, используйте LOGGING для определения словаря параметров. В этих параметрах описываются регистраторы, обработчики, фильтры и форматтеры, которые нужны в конфигурации, а также уровни ведения журнала и другие свойства этих компонентов.
По умолчанию параметр LOGGING объединяется с конфигурацией ведения журнала Django по умолчанию по следующей схеме.
Если ключ disable_existing_loggers в словаре dictConfig параметра LOGGING имеет значение True (это значение dictConfig по умолчанию, если ключ отсутствует), все регистраторы из конфигурации по умолчанию будут отключены. Отключённые регистраторы не удаляются: они продолжают существовать, но молча отбрасывают все записываемые в них данные и даже не передают записи родительскому регистратору. Поэтому следует с большой осторожностью использовать 'disable_existing_loggers': True — скорее всего, это не то, что вам нужно. Вместо этого можно задать disable_existing_loggers значение False и переопределить некоторые или все регистраторы по умолчанию; либо задать LOGGING_CONFIG значение None и самостоятельно настроить ведение журнала.
Ведение журнала настраивается в рамках общей функции setup() Django. Поэтому можно быть уверенным, что регистраторы всегда готовы к использованию в коде проекта.
Примеры
Полная документация по формату dictConfig — лучший источник информации о словарях конфигурации ведения журнала. Чтобы показать, что можно сделать, приведём несколько примеров.
Для начала рассмотрим небольшую конфигурацию, которая выводит все сообщения журнала в консоль:
settings.pyimport os
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
"root": {
"handlers": ["console"],
"level": "WARNING",
},
}
Эта конфигурация настраивает родительский регистратор root на отправку сообщений с уровнем WARNING и выше обработчику консоли. Изменив уровень на INFO или DEBUG, можно выводить больше сообщений. Это может быть полезно при разработке.
Затем можно добавить более точную настройку ведения журнала. Вот пример того, как настроить систему на вывод большего количества сообщений только от регистратора с именем django:
settings.pyimport 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.pyLOGGING = {
"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.pyLOGGING = {
"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, описывающая сведения, которые будут выводиться в каждой строке журнала. Полный список доступных для вывода сведений приведён в разделе Объекты форматирования. -
verbose, который выводит название уровня ведения журнала, сообщение, а также время, процесс, поток и модуль, создавшие сообщение.
-
-
Определяет два фильтра:
-
project.logging.SpecialFilterс псевдонимомspecial. Если этому фильтру требуются дополнительные аргументы, их можно указать в виде дополнительных ключей в словаре конфигурации фильтра. В этом случае при созданииSpecialFilterаргументуfooбудет присвоено значениеbar. -
django.utils.log.RequireDebugTrue, который пропускает записи, еслиDEBUGимеет значениеTrue.
-
-
Определяет два обработчика:
-
console—StreamHandler, который выводит вsys.stderrвсе сообщения уровняINFOили выше. Этот обработчик использует формат выводаsimple. -
mail_admins—AdminEmailHandler, который отправляет по электронной почте администраторам сайтаADMINSвсе сообщения уровняERRORили выше. Этот обработчик использует фильтрspecial.
-
-
Настраивает три регистратора:
-
django, который передаёт все сообщения обработчикуconsole. -
django.request, который передаёт все сообщения уровняERRORобработчикуmail_admins. Кроме того, для этого регистратора отключена передача сообщений родительским регистраторам. Это означает, что сообщения, записанные вdjango.request, не будут обработаны регистраторомdjango. -
myproject.custom, который передаёт все сообщения уровняINFOили выше, прошедшие фильтрspecial, двум обработчикам —consoleиmail_admins. Это означает, что все сообщения уровняINFOили выше будут выведены в консоль; сообщенияERRORиCRITICALтакже будут отправлены по электронной почте.
-
Пользовательская конфигурация ведения журнала
Если вы не хотите использовать формат dictConfig Python для настройки регистратора, можно задать собственную схему конфигурации.
Параметр LOGGING_CONFIG определяет вызываемый объект, который будет использоваться для настройки регистраторов Django. По умолчанию он указывает на функцию Python logging.config.dictConfig(). Однако, если вы хотите использовать другой процесс настройки, можно выбрать любой другой вызываемый объект, принимающий один аргумент. При настройке ведения журнала содержимое LOGGING будет передано в качестве значения этого аргумента.
Отключение настройки ведения журнала
Если вы совсем не хотите настраивать ведение журнала (или хотите настроить его вручную своим способом), задайте параметру LOGGING_CONFIG значение None. Это отключит процесс настройки ведения журнала Django по умолчанию.
Если задать параметру LOGGING_CONFIG значение None, отключится только автоматическая настройка, но не само ведение журнала. Даже если отключить процесс настройки, Django продолжит вызывать функции ведения журнала, используя поведение по умолчанию, если оно определено.
Вот пример, который отключает настройку ведения журнала Django, а затем настраивает ведение журнала вручную:
settings.pyLOGGING_CONFIG = None import logging.config logging.config.dictConfig(...)
Обратите внимание: процесс настройки по умолчанию вызывает LOGGING_CONFIG только после полной загрузки параметров. В отличие от него, ручная настройка ведения журнала в файле параметров немедленно загружает конфигурацию. Поэтому конфигурация ведения журнала должна располагаться после всех параметров, от которых она зависит.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/topics/logging/