Ведение журналов
Быстрый обзор ведения журналов
Django использует встроенный модуль Python logging для выполнения системного ведения журналов. Подробное описание использования этого модуля можно найти в собственной документации Python. Однако, если вы никогда не использовали фреймворк ведения журналов Python (или даже если использовали), вот краткий обзор.
Актеры
Настройка ведения журналов Python состоит из четырех частей:
Логгеры
Логгер — это точка входа в систему ведения журналов. Каждый логгер — это именованное хранилище, в которое можно записывать сообщения для обработки.
Логгер настроен с уровнем журнала. Этот уровень журнала описывает степень важности сообщений, которые будет обрабатывать логгер. Python определяет следующие уровни журналов:
-
DEBUG: Информация низкого уровня о системе для отладки -
INFO: Общая информация о системе -
WARNING: Информация об обнаруженной небольшой проблеме. -
ERROR: Информация об обнаруженной значительной проблеме. -
CRITICAL: Информация об обнаруженной критической проблеме.
Каждое сообщение, записанное в логгер, является записью журнала. Каждая запись журнала также имеет уровень журнала, указывающий на важность конкретного сообщения. Запись журнала также может содержать полезные метаданные, описывающие событие, которое регистрируется. Это могут быть такие детали, как стек вызовов или код ошибки.
Когда сообщение передаётся логгеру, уровень журнала сообщения сравнивается с уровнем журнала логгера. Если уровень журнала сообщения соответствует или превышает уровень журнала самого логгера, сообщение будет подвергнуто дальнейшей обработке. Если нет, сообщение будет проигнорировано.
После того, как логгер определил, что сообщение нужно обработать, оно передаётся обработчику.
Обработчики
Обработчик — это механизм, определяющий, что происходит с каждым сообщением в логгер. Он описывает определённое поведение ведения журнала, например, запись сообщения на экран, в файл или в сетевой сокет.
Как и логгеры, обработчики также имеют уровень журнала. Если уровень журнала записи журнала не соответствует или не превышает уровень обработчика, обработчик проигнорирует сообщение.
Логгер может иметь несколько обработчиков, и каждый обработчик может иметь разный уровень журнала. Таким образом, можно предоставить различные формы уведомлений в зависимости от важности сообщения. Например, вы можете установить один обработчик, который перенаправляет ERROR и CRITICAL сообщения в службу оповещения, а второй обработчик записывает все сообщения (включая ERROR и CRITICAL сообщения) в файл для последующего анализа.
Фильтры
Фильтр используется для дополнительного управления тем, какие записи журнала передаются от логгера к обработчику.
По умолчанию любое сообщение журнала, удовлетворяющее требованиям уровня журнала, будет обработано. Однако, установив фильтр, вы можете добавить дополнительные критерии к процессу ведения журнала. Например, вы можете установить фильтр, который позволит отображаться только ERROR сообщениям из определённого источника.
Фильтры также могут использоваться для модификации записи журнала до её отправки. Например, вы можете написать фильтр, который понижает ERROR записи журнала до WARNING записей, если выполнены определённые критерии.
Фильтры могут устанавливаться на логгеры или обработчики; несколько фильтров могут использоваться в цепочке для выполнения нескольких действий фильтрации.
Форматировщики
В конечном счёте, запись журнала должна быть представлена в виде текста. Форматировщики описывают точный формат этого текста. Форматировщик обычно состоит из строки форматирования Python, содержащей атрибуты LogRecord; однако, вы также можете написать пользовательские форматировщики для реализации специфичного форматирования.
Использование ведения журнала
После настройки логгеров, обработчиков, фильтров и форматировщиков необходимо разместить вызовы ведения журнала в вашем коде. Использование фреймворка ведения журнала очень просто. Вот пример:
# 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'),
},
},
}
Стандартная конфигурация ведения журнала Django изменилась. См. заметки к выпуску для описания изменений.
Наконец, вот пример довольно сложной настройки ведения журнала:
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, который будет выводить любое сообщение уровняDEBUG(или выше) в stderr. Этот обработчик использует формат выводаsimple. -
mail_admins, AdminEmailHandler, который будет отправлять электронное письмо с любым сообщением уровня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. Вот пример, который отключает конфигурацию ведения журнала 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.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. Существует подлоггер для каждого подтипа 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.db.backends.schema
Регистрирует SQL-запросы, выполняемые во время изменений схемы в базе данных фреймворком миграций. Обратите внимание, что он не будет регистрировать запросы, выполняемые RunPython.
Обработчики
Django предоставляет один обработчик логов в дополнение к тем, которые предоставляет модуль Python logging.
-
class AdminEmailHandler(include_html=False, email_backend=None)[source] -
Этот обработчик отправляет электронное письмо администраторам сайта для каждого сообщения журнала, которое он получает.
Если запись журнала содержит атрибут
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.-
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(обрабатывает все сообщения) отправляет все сообщения уровняINFOи выше в консоль. - Логгер
py.warnings(обрабатывает сообщения изwarnings.warn()отправляет сообщения в консоль.
Когда DEBUG равен False:
- Логгер
djangoотправляет сообщения уровняERRORилиCRITICALвAdminEmailHandler.
Конфигурация логирования по умолчанию Django изменилась. Смотрите примечания к выпуску для описания изменений.
См. также Настройка логирования, чтобы узнать, как вы можете дополнить или заменить эту конфигурацию логирования по умолчанию.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/topics/logging/