Ведение журнала
Краткий обзор ведения журнала
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'),
},
},
}
Наконец, вот пример довольно сложной настройки ведения журнала:
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. Вот пример, который отключает конфигурацию логгирования 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)[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, можно переопределить используемый обработчик электронной почты, как показано ниже:'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отправляет сообщения в иерархииdjango(кромеdjango.server) уровняINFOили выше на консоль.
Когда DEBUG равно False:
- Логгер
djangoотправляет сообщения в иерархииdjango(кромеdjango.server) уровняERRORилиCRITICALнаAdminEmailHandler.
Независимо от значения DEBUG:
- Логгер django.server отправляет сообщения уровня
INFOили выше на консоль.
Все логгеры, кроме django.server, передают логирование своим родителям, до корневого логгера django . Обработчики console и mail_admins прикреплены к корневому логгеру для обеспечения описанного выше поведения.
См. также Настройка логирования, чтобы узнать, как дополнить или заменить эту стандартную конфигурацию логирования.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.1/topics/logging/