Ведение журнала
Программисты Python часто используют print() в своём коде в качестве быстрого и удобного инструмента отладки. Использование системы ведения журнала требует немного больше усилий, но она гораздо элегантнее и гибче. Помимо использования для отладки, ведение журнала также может предоставить вам больше и более структурированной информации о состоянии и работоспособности вашего приложения.
Обзор
Django использует и расширяет встроенный модуль Python logging для выполнения системного ведения журнала. Этот модуль подробно описан в документации Python; этот раздел предоставляет краткий обзор.
Участники процесса
Настройка ведения журнала Python состоит из четырёх частей:
Журнализаторы
Журнализатор — это точка входа в систему ведения журнала. Каждый журнализатор — это именованное хранилище, в которое можно записывать сообщения для обработки.
Журнализатор настроен на уровень журнала. Этот уровень журнала описывает степень важности сообщений, которые будет обрабатывать журнализатор. Python определяет следующие уровни журнала:
-
DEBUG: Информация низкого уровня для целей отладки -
INFO: Общая системная информация -
WARNING: Информация, описывающая незначительную проблему, которая произошла. -
ERROR: Информация, описывающая серьёзную проблему, которая произошла. -
CRITICAL: Информация, описывающая критическую проблему, которая произошла.
Каждое сообщение, которое записывается в журнализатор, является записью журнала. У каждой записи журнала также есть уровень журнала, указывающий степень важности этого конкретного сообщения. Запись журнала также может содержать полезные метаданные, описывающие событие, которое регистрируется. Это может включать такие подробности, как трассировка стека или код ошибки.
Когда сообщению передаётся журнализатор, уровень журнала сообщения сравнивается с уровнем журнала самого журнализатора. Если уровень журнала сообщения соответствует или превышает уровень журнала самого журнализатора, сообщение будет подвергнуто дальнейшей обработке. Если нет, сообщение будет проигнорировано.
После того, как журнализатор определил, что сообщение нужно обработать, оно передаётся обработчику.
Обработчики
Обработчик — это механизм, который определяет, что происходит с каждым сообщением в журнализаторе. Он описывает определённое поведение ведения журнала, например, запись сообщения на экран, в файл или в сетевой сокет.
Как и журнализаторы, у обработчиков также есть уровень журнала. Если уровень журнала записи журнала не соответствует или не превышает уровень обработчика, обработчик проигнорирует сообщение.
Журнализатор может иметь несколько обработчиков, и каждый обработчик может иметь другой уровень журнала. Таким образом, можно предоставить различные формы уведомлений в зависимости от важности сообщения. Например, можно установить один обработчик, который пересылает сообщения ERROR и CRITICAL в службу оповещения, а второй обработчик записывает все сообщения (включая сообщения ERROR и CRITICAL ) в файл для последующего анализа.
Фильтры
Фильтр используется для дополнительного управления тем, какие записи журнала передаются от журнализатора к обработчику.
По умолчанию любое сообщение журнала, соответствующее требованиям уровня журнала, будет обработано. Однако, установив фильтр, можно добавить дополнительные критерии к процессу ведения журнала. Например, можно установить фильтр, который позволяет выводить только сообщения ERROR из определённого источника.
Фильтры также могут использоваться для изменения записи журнала перед выводом. Например, можно написать фильтр, который понижает уровень ERROR до уровня WARNING , если выполнены определённые критерии.
Фильтры можно устанавливать на журнализаторы или на обработчики; несколько фильтров могут использоваться в цепочке для выполнения нескольких действий фильтрации.
Форматировщики
В конечном счёте, запись журнала должна быть представлена в виде текста. Форматировщики описывают точный формат этого текста. Форматировщик обычно состоит из строки форматирования Python, содержащей атрибуты записи журнала; однако вы также можете написать пользовательские форматировщики для реализации определённого поведения форматирования.
Безопасность при ведении журнала
Система ведения журнала обрабатывает потенциально конфиденциальную информацию. Например, запись журнала может содержать информацию об веб-запросе или трассировке стека, а некоторые данные, которые вы собираете в своих собственных журнализаторах, также могут иметь последствия для безопасности. Вам нужно убедиться, что вы знаете:
- какая информация собирается
- где она будет храниться
- как она будет передаваться
- кто может к ней получить доступ.
Чтобы помочь контролировать сбор конфиденциальной информации, вы можете явно указать определённую конфиденциальную информацию, которая должна быть исключена из отчётов об ошибках — ознакомьтесь с тем, как фильтровать отчёты об ошибках.
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 является лучшим источником информации о словарях конфигурации ведения журнала. Однако, чтобы дать вам представление о том, что возможно, вот несколько примеров.
Для начала вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала в консоль:
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 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 и затем настраивает логирование вручную:
LOGGING_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.0/topics/logging/