Ведение журнала
Программисты 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 в словаре LOGGING установлен в значение True (что является значением по умолчанию dictConfig если ключ отсутствует), то все журнализаторы из стандартной конфигурации будут отключены. Отключенные журнализаторы не являются удалёнными; журнализатор всё ещё будет существовать, но будет молча отбрасывать всё, что в него записывается, даже не распространяя записи в родительский журнализатор. Поэтому нужно очень осторожно использовать 'disable_existing_loggers': True; это, вероятно, не то, что вам нужно. Вместо этого вы можете установить disable_existing_loggers в False и переопределить некоторые или все стандартные журнализаторы; или вы можете установить LOGGING_CONFIG в None и обработать конфигурацию ведения журнала самостоятельно.
Ведение журнала настраивается как часть общей функции Django setup(). Поэтому вы можете быть уверены, что журнализаторы всегда готовы к использованию в вашем проекте кода.
Примеры
Полная документация по формату 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 version 1'. В настоящее время это единственный формат версии dictConfig.
-
Определяет два форматера:
-
simple, который выводит имя уровня лога (например,DEBUG) и сообщение лога.Строка
format— это обычная строка форматирования Python, описывающая детали, которые должны быть выведены в каждой строке лога. Полный список деталей, которые могут быть выведены, можно найти в Объектах Форматирования. -
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сообщения также будут отправлены по электронной почте.
-
Настройка логгирования
Если вы не хотите использовать формат 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/4.1/topics/logging/