Ведение журнала
Краткий обзор ведения журнала
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 (что является значением по умолчанию), все логгеры из конфигурации по умолчанию будут отключены. Отключенные логгеры не являются удалёнными; логгер по-прежнему существует, но безмолвно отбрасывает всё, что записывается в него, не распространяя даже записи родительскому логгере. Следовательно, нужно быть очень осторожным с 'disable_existing_loggers': True; это, вероятно, не то, что вы хотите. Вместо этого вы можете установить disable_existing_loggers в False и переопределить некоторые или все стандартные логгеры; или вы можете установить LOGGING_CONFIG в None и самостоятельно обработать конфигурацию ведения журнала.
Ведение журнала настраивается как часть общей функции Django setup(). Таким образом, можно гарантировать, что логгеры всегда будут готовы к использованию в коде вашего проекта.
Примеры
Полная документация по формату dictConfig — лучший источник информации о словарях конфигурации ведения журнала. Однако, чтобы дать вам представление о возможностях, вот несколько примеров.
Во-первых, вот простая конфигурация, которая записывает весь журнал логгера 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.
Во-вторых, вот пример того, как настроить систему ведения журнала для вывода журнала Django в консоль. Это может быть полезно во время локального разработки.
По умолчанию эта конфигурация отправляет только сообщения уровня INFO и выше в консоль (то же самое, что и в стандартной конфигурации ведения журнала Django, за исключением того, что по умолчанию записи журнала отображаются, только если DEBUG=True). Django не регистрирует множество таких сообщений. Однако с помощью этой конфигурации вы также можете установить переменную среды DJANGO_LOG_LEVEL=DEBUG для отображения всех отладочных журналов Django, которые очень подробны, поскольку включают все запросы к базе данных:
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}
Наконец, вот пример довольно сложной настройки ведения журнала:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'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. Вот пример, который отключает конфигурацию логгирования 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.
Сообщения в этот логгер содержат следующий дополнительный контекст:
-
status_code: Код HTTP-ответа, связанный с запросом. -
request: Объект запроса, который сгенерировал сообщение логгирования.
django.server
Сообщения журнала, связанные с обработкой запросов, полученных сервером, вызванным командой runserver. HTTP-ответы 5XX записываются как сообщения ERROR, 4XX — как сообщения WARNING, а всё остальное — как INFO.
Сообщения в этот логгер содержат следующий дополнительный контекст:
-
status_code: Код HTTP-ответа, связанный с запросом. -
request: Объект запроса, который сгенерировал сообщение логгирования.
django.template
Сообщения журнала, связанные с рендерингом шаблонов.
- Отсутствующие переменные контекста регистрируются как сообщения
DEBUG. - Необработанные исключения, возникшие во время рендеринга
{% include %}, регистрируются как сообщенияWARNINGпри отключенном режиме отладки (полезно, так как{% include %}в этом случае скрывает исключение и возвращает пустую строку).
django.db.backends
Сообщения, связанные с взаимодействием кода с базой данных. Например, каждое SQL-выражение уровня приложения, выполненное запросом, регистрируется на уровне DEBUG в этом логгере.
Сообщения в этот логгер содержат следующий дополнительный контекст:
-
duration: Время, затраченное на выполнение SQL-выражения. -
sql: SQL-выражение, которое было выполнено. -
params: Параметры, которые использовались в вызове SQL.
По соображениям производительности SQL-логгирование включено только тогда, когда settings.DEBUG установлено в True, независимо от уровня логгирования или установленных обработчиков.
Это логгирование не включает инициализацию на уровне фреймворка (например, SET TIMEZONE) или запросы управления транзакциями (например, BEGIN, COMMIT, и ROLLBACK). Включите логгирование запросов в своей базе данных, если хотите просмотреть все запросы к базе данных.
django.security.*
Логгеры безопасности получат сообщения при любом возникновении SuspiciousOperation и других связанных с безопасностью ошибках. Существует подлоггер для каждого подтипа ошибки безопасности, включая все SuspiciousOperations. Уровень события логгирования зависит от места обработки исключения. Большинство случаев логгируются как предупреждение, а любые SuspiciousOperations, которые достигают обработчика 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.
-
class AdminEmailHandler(include_html=False, email_backend=None)[source] -
Этот обработчик отправляет электронное письмо администраторам сайта
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, можно переопределить используемый backend для отправки писем, как показано ниже:'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'email_backend': 'django.core.mail.backends.filebased.EmailBackend', } },По умолчанию используется экземпляр email backend, указанный в
EMAIL_BACKEND.-
send_mail(subject, message, *args, **kwargs)[source] -
Отправляет письма администраторам. Чтобы настроить это поведение, можно создать подкласс класса
AdminEmailHandlerи переопределить этот метод.
-
Фильтры
Django предоставляет некоторые фильтры журналов в дополнение к тем, которые предоставляются модулем Python logging.
-
class CallbackFilter(callback)[source] -
Этот фильтр принимает функцию обратного вызова (которая должна принимать один аргумент, запись, которая должна быть записана в журнал), и вызывает её для каждой записи, которая проходит фильтр. Обработка этой записи не будет продолжена, если обратный вызов вернёт 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[source] -
Этот фильтр пропускает записи только тогда, когда значение 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[source] -
Этот фильтр аналогичен
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 Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/topics/logging/