Ведение журнала
Краткий обзор ведения журнала
Django использует встроенный модуль Python logging для выполнения системного ведения журнала. Подробное описание использования этого модуля можно найти в документации Python. Однако, если вы никогда не использовали фреймворк ведения журнала Python (или даже если использовали), вот краткий обзор.
Участники процесса
Настройка ведения журнала в Python состоит из четырёх частей:
Журнализаторы
Журнализатор — это точка входа в систему ведения журнала. Каждый журнализатор — это именованный контейнер, в который можно записывать сообщения для обработки.
Журнализатор настраивается с уровнем журнала. Этот уровень журнала описывает степень важности сообщений, которые будет обрабатывать журнализатор. Python определяет следующие уровни журнала:
-
DEBUG: Информация низкого уровня о системе для отладки -
INFO: Общая информация о системе -
WARNING: Информация об обнаруженной незначительной проблеме. -
ERROR: Информация об обнаруженной значительной проблеме. -
CRITICAL: Информация об обнаруженной критической проблеме.
Каждое сообщение, отправляемое в журнализатор, — это Запись журнала. Каждая запись журнала также имеет уровень журнала, указывающий степень важности конкретного сообщения. Запись журнала также может содержать полезные метаданные, описывающие событие, которое регистрируется. Это может включать подробности, такие как трассировка стека или код ошибки.
Когда сообщение передаётся журнализатору, уровень журнала сообщения сравнивается с уровнем журнала самого журнализатора. Если уровень журнала сообщения соответствует или превышает уровень журнала самого журнализатора, сообщение будет подвергнуто дальнейшей обработке. В противном случае сообщение будет проигнорировано.
После того, как журнализатор определил, что сообщение необходимо обработать, оно передаётся Обработчику.
Обработчики
Обработчик — это механизм, определяющий, что происходит с каждым сообщением в журнализаторе. Он описывает конкретное поведение ведения журнала, такое как запись сообщения на экран, в файл или в сетевой сокет.
Как и журнализаторы, обработчики также имеют уровень журнала. Если уровень журнала записи журнала не соответствует или не превышает уровень обработчика, обработчик проигнорирует сообщение.
Журнализатор может иметь несколько обработчиков, и каждый обработчик может иметь различный уровень журнала. Таким образом, можно предоставить различные формы уведомлений в зависимости от важности сообщения. Например, можно установить один обработчик, который пересылает сообщения ERROR и CRITICAL в службу оповещения, а второй обработчик записывает все сообщения (включая сообщения ERROR и CRITICAL) в файл для последующего анализа.
Фильтры
Фильтр используется для дополнительного управления тем, какие записи журнала передаются из журнализатора в обработчик.
По умолчанию любое сообщение журнала, которое соответствует требованиям уровня журнала, будет обработано. Однако, установив фильтр, можно добавить дополнительные критерии к процессу ведения журнала. Например, можно установить фильтр, который разрешает передавать только сообщения ERROR из определённого источника.
Фильтры также могут использоваться для изменения записи журнала перед её отправкой. Например, можно написать фильтр, который понижает уровень ERROR записей журнала до WARNING записей, если выполняются определённые критерии.
Фильтры можно устанавливать на журнализаторы или на обработчики; можно использовать несколько фильтров в цепочке для выполнения нескольких фильтрующих действий.
Форматировщики
В конечном итоге запись журнала должна быть представлена в виде текста. Форматировщики описывают точный формат этого текста. Форматировщик обычно состоит из строки форматирования Python, содержащей атрибуты записи журнала; однако вы также можете создавать собственные форматировщики для реализации специфического поведения форматирования.
Использование ведения журнала
После настройки журнализаторов, обработчиков, фильтров и форматировщиков необходимо добавить вызовы ведения журнала в свой код. Работа с фреймворком ведения журнала происходит следующим образом:
# import the logging library
import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
def my_view(request, arg1, arg):
...
if bad_mojo:
# Log an error message
logger.error('Something went wrong!')
И это всё! Каждый раз, когда выполняется условие bad_mojo, запись журнала об ошибке будет записана.
Именование журнализаторов
Вызов logging.getLogger() получает (создаёт, если необходимо) экземпляр журнализатора. Экземпляр журнализатора идентифицируется по имени. Это имя используется для идентификации журнализатора в целях конфигурации.
По соглашению, имя журнализатора обычно __name__, имя модуля Python, содержащего журнализатор. Это позволяет фильтровать и обрабатывать вызовы ведения журнала на уровне каждого модуля. Однако, если у вас есть другой способ организации сообщений ведения журнала, вы можете указать любое имя, разделённое точками, для идентификации своего журнализатора:
# Get an instance of a specific named logger
logger = logging.getLogger('project.interesting.stuff')
Точечные пути имён журнализаторов определяют иерархию. Журнализатор project.interesting считается родителем журнализатора project.interesting.stuff; журнализатор project является родителем журнализатора project.interesting.
Зачем важна иерархия? Потому что журнализаторы могут быть настроены на распространение своих вызовов ведения журнала своим родителям. Таким образом, вы можете определить один набор обработчиков в корне дерева журнализаторов и захватить все вызовы ведения журнала в поддереве журнализаторов. Журнализатор, определённый в пространстве имён project, будет ловить все сообщения ведения журнала, выпущенные в журнализаторах project.interesting и project.interesting.stuff.
Это распространение можно контролировать на уровне каждого журнализатора. Если вы не хотите, чтобы определённый журнализатор распространял вызовы ведения журнала своим родителям, вы можете отключить это поведение.
Выполнение вызовов ведения журнала
Экземпляр журнализатора содержит метод входа для каждого из стандартных уровней журнала:
logger.debug()logger.info()logger.warning()logger.error()logger.critical()
Доступны ещё два вызова ведения журнала:
-
logger.log(): Ручное отправление сообщения ведения журнала с заданным уровнем журнала. -
logger.exception(): Создание сообщения ведения журнала уровняERROR, содержащего текущий кадр стека исключений.
Настройка ведения журнала
Конечно, недостаточно просто добавить вызовы ведения журнала в свой код. Вам также нужно настроить журнализаторы, обработчики, фильтры и форматировщики, чтобы обеспечить полезный вывод логов.
Библиотека ведения журнала 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 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, описывающая детали, которые должны быть выведены в каждой строке логгирования. Полный список деталей, которые можно вывести, можно найти в Объектах форматирования. -
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. Вот пример, который отключает конфигурацию логгирования Django, а затем настраивает логгирование вручную:
LOGGING_CONFIG = None import logging.config logging.config.dictConfig(...)
Установка LOGGING_CONFIG на значение None означает только то, что процесс автоматической конфигурации отключен, а не само логгирование. Если вы отключите процесс конфигурации, Django по-прежнему будет выполнять вызовы логгирования, используя поведение по умолчанию, которое определено.
Расширения логгирования Django
Django предоставляет ряд утилит для обработки особых требований к логгированию в среде веб-сервера.
Логгеры
Django предоставляет несколько встроенных логгеров.
django
Всеобщий логгер для сообщений в иерархии django. Сообщения не публикуются с этим именем, а вместо этого используются один из логгеров ниже.
django.request
Сообщения лога, связанные с обработкой запросов. 5XX ответы генерируются как сообщения ERROR; 4XX ответы генерируются как сообщения WARNING. Запросы, которые регистрируются в логгере django.security, не регистрируются в логгере django.request.
Сообщения в этом логгере содержат следующий дополнительный контекст:
-
status_code: Код HTTP-ответа, связанный с запросом. -
request: Объект запроса, сгенерировавший сообщение лога.
django.server
Сообщения журнала, связанные с обработкой запросов, полученных сервером, вызванным командой runserver. HTTP-ответы 5XX регистрируются как сообщения ERROR, 4XX ответы регистрируются как сообщения WARNING, а все остальное — как INFO.
Сообщения в этом логгере содержат следующий дополнительный контекст:
-
status_code: Код HTTP-ответа, связанный с запросом. -
request: Объект запроса, сгенерировавший сообщение лога.
django.template
Сообщения журнала, связанные с рендерингом шаблонов.
- Отсутствующие переменные контекста регистрируются как сообщения
DEBUG.
django.db.backends
Сообщения, связанные с взаимодействием кода с базой данных. Например, каждое SQL-утверждение уровня приложения, выполняемое запросом, регистрируется на уровне DEBUG в этом логгере.
Сообщения в этом логгере содержат следующий дополнительный контекст:
-
duration: Время выполнения SQL-запроса. -
sql: SQL-запрос, который был выполнен. -
params: Параметры, которые были использованы в вызове SQL.
По соображениям производительности, логгирование SQL включается только тогда, когда settings.DEBUG установлено в значение True, независимо от уровня логгирования или установленных обработчиков.
Это логгирование не включает инициализацию на уровне фреймворка (например, SET TIMEZONE) или запросы управления транзакциями (например, BEGIN, COMMIT, и ROLLBACK). Включите логгирование запросов в вашей базе данных, если хотите просмотреть все запросы к базе данных.
django.security.*
Логгеры безопасности получат сообщения при любом возникновении SuspiciousOperation и других ошибок, связанных с безопасностью. Существует подлоггер для каждого подтипа ошибки безопасности, включая все SuspiciousOperation. Уровень события лога зависит от места обработки исключения. Большинство случаев регистрируются как предупреждения, а любой SuspiciousOperation, достигающий обработчика WSGI, регистрируется как ошибка. Например, когда заголовок HTTP Host включается в запрос от клиента, который не соответствует ALLOWED_HOSTS, Django вернет ответ 400, и сообщение об ошибке будет записано в логгер django.security.DisallowedHost.
Эти события лога по умолчанию попадают в логгер django, который отправляет сообщения об ошибках администраторам, когда DEBUG=False. Запросы, которые приводят к ответу 400 из-за SuspiciousOperation, не будут записаны в логгер django.request, а только в логгер django.security.
Чтобы отключить определенный тип SuspiciousOperation, можно переопределить соответствующий логгер, следуя этому примеру:
'handlers': {
'null': {
'class': 'logging.NullHandler',
},
},
'loggers': {
'django.security.DisallowedHost': {
'handlers': ['null'],
'propagate': False,
},
},
Другие логгеры django.security, не основанные на SuspiciousOperation:
-
django.security.csrf: Для ошибок CSRF.
django.db.backends.schema
Записывает SQL-запросы, выполняемые во время изменений схемы базы данных фреймворком миграций. Обратите внимание, что он не будет записывать запросы, выполняемые RunPython. Сообщения в этом логгере содержат params и sql в контексте дополнительных данных (но, в отличие от django.db.backends, без длительности). Значения имеют такое же значение, как и описано в django.db.backends.
Обработчики
Django предоставляет один обработчик логов дополнительно к тем, которые предоставляет модуль Python logging.
-
class AdminEmailHandler(include_html=False, email_backend=None, reporter_class=None) -
Этот обработчик отправляет электронное письмо на сайт
ADMINSдля каждого сообщения журнала.Если запись журнала содержит атрибут
request, полные детали запроса будут включены в электронное письмо. В теме письма будет указана фраза «внутренний IP», если IP-адрес клиента находится в настройкеINTERNAL_IPS; в противном случае будет указана фраза «внешний IP».Если запись журнала содержит информацию об отладке, эта информация будет включена в электронное письмо.
Аргумент
include_htmlметодаAdminEmailHandlerиспользуется для управления включением в электронное письмо со следом ошибки HTML-приложения, содержащего полное содержимое страницы отладки веб-сайта, которая была бы создана, если быDEBUGбылTrue. Чтобы задать это значение в своей конфигурации, включите его в определение обработчика дляdjango.utils.log.AdminEmailHandler, как показано ниже:'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'include_html': True, } },Обратите внимание, что этот HTML-вариант письма содержит полный след ошибки, с именами и значениями локальных переменных на каждом уровне стека, а также значениями ваших настроек Django. Эта информация потенциально может быть очень чувствительной, и вы можете не захотеть отправлять её по электронной почте. Рассмотрите использование инструмента, такого как Sentry, чтобы получить лучшее из обоих миров — богатую информацию полных следов ошибок и безопасность отсутствия отправки этой информации по электронной почте. Вы также можете явно указать определённую конфиденциальную информацию, чтобы она не включалась в сообщения об ошибках — узнайте больше на странице Фильтрация сообщений об ошибках.
Указывая аргумент
email_backendметодаAdminEmailHandler, можно переопределить используемый обработчик электронной почты, как показано ниже:'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'email_backend': 'django.core.mail.backends.filebased.EmailBackend', } },По умолчанию будет использоваться экземпляр обработчика электронной почты, указанный в
EMAIL_BACKEND.Аргумент
reporter_classметодаAdminEmailHandlerпозволяет указать подклассdjango.views.debug.ExceptionReporterдля настройки текста следа ошибки, отправляемого в теле электронного письма. Укажите путь к импорту класса, который вы хотите использовать, как показано ниже:'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'include_html': True, 'reporter_class': 'somepackage.error_reporter.CustomErrorReporter' } },Добавлено в Django 3.0:Добавлен аргумент
reporter_class.-
send_mail(subject, message, *args, **kwargs) -
Отправляет электронные письма администраторам. Для настройки этого поведения вы можете создать подкласс класса
AdminEmailHandlerи переопределить этот метод.
-
Фильтры
Django предоставляет некоторые фильтры логов дополнительно к тем, которые предоставляет модуль Python logging.
-
class CallbackFilter(callback) -
Этот фильтр принимает функцию обратного вызова (которая должна принимать один аргумент, запись, подлежащую регистрации), и вызывает её для каждой записи, которая проходит через фильтр. Обработка этой записи не будет продолжена, если функция обратного вызова вернёт False.
Например, чтобы отфильтровать
UnreadablePostError(возникает, когда пользователь отменяет загрузку) из писем администратора, вы создаёте функцию фильтра:from django.http import UnreadablePostError def skip_unreadable_post(record): if record.exc_info: exc_type, exc_value = record.exc_info[:2] if isinstance(exc_value, UnreadablePostError): return False return Trueи затем добавляете её в конфигурацию логгирования:
'filters': { 'skip_unreadable_posts': { '()': 'django.utils.log.CallbackFilter', 'callback': skip_unreadable_post, } }, 'handlers': { 'mail_admins': { 'level': 'ERROR', 'filters': ['skip_unreadable_posts'], 'class': 'django.utils.log.AdminEmailHandler' } },
-
class RequireDebugFalse -
Этот фильтр пропускает записи только тогда, когда значение settings.DEBUG равно False.
Этот фильтр используется в стандартной конфигурации
LOGGINGдля того, чтобы гарантировать, чтоAdminEmailHandlerотправляет письма об ошибках администраторам только тогда, когдаDEBUGравноFalse:'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse', } }, 'handlers': { 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' } },
-
class RequireDebugTrue -
Этот фильтр аналогичен
RequireDebugFalse, за исключением того, что записи пропускаются только тогда, когдаDEBUGравноTrue.
Стандартная конфигурация логгирования Django
По умолчанию Django настраивает следующее логгирование:
Когда DEBUG равно True:
- Логгер
djangoотправляет сообщения в иерархииdjango(кромеdjango.server) на уровнеINFOили выше в консоль.
Когда DEBUG равно False:
- Логгер
djangoотправляет сообщения в иерархииdjango(кромеdjango.server) на уровнеERRORилиCRITICALвAdminEmailHandler.
Независимо от значения DEBUG:
- Логгер django.server отправляет сообщения на уровне
INFOили выше в консоль.
Все логгеры, кроме django.server, передают логгирование своим родительским элементам, до корневого логгера django.
Обработчики console и mail_admins прикреплены к корневому логгере для обеспечения описанного выше поведения.
См. также Настройка логгирования, чтобы узнать, как дополнить или заменить эту конфигурацию логгирования по умолчанию, определённую в django/utils/log.py.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/3.0/topics/logging/