Ведение журнала
Краткий обзор ведения журнала
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 (что является значением по умолчанию dictConfig, если ключ отсутствует), все логгеры из стандартной конфигурации будут отключены. Отключенные логгеры отличаются от удалённых; логгер всё ещё существует, но молча игнорирует всё, что записывается в него, не распространяя даже записи в родительский логгер. Поэтому следует очень осторожно использовать 'disable_existing_loggers': True; вероятно, это не то, что вам нужно. Вместо этого можно установить disable_existing_loggers в значение False и переопределить некоторые или все стандартные логгеры; или вы можете установить LOGGING_CONFIG в значение None и обработать конфигурацию логирования самостоятельно.
Логирование настраивается как часть общей функции Django setup(). Поэтому вы можете быть уверены, что логгеры всегда будут готовы к использованию в вашем проекте.
Примеры
Полная документация по dictConfig — лучший источник информации о словарях конфигурации логирования. Однако, чтобы дать вам представление о возможностях, вот несколько примеров.
Для начала, вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
}
Эта конфигурация настраивает родительский логгер root на отправку сообщений с уровнем WARNING и выше в обработчик консоли. Изменив уровень на INFO или DEBUG, можно отображать больше сообщений. Это может быть полезно во время разработки.
Далее, можно добавить более точное логирование. Вот пример того, как сделать систему логирования, чтобы она отображала больше сообщений только из логгера с именем django:
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
'propagate': False,
},
},
}
По умолчанию эта конфигурация отправляет сообщения из логгера django уровня INFO или выше в консоль. Это соответствует уровню по умолчанию для конфигурации логгирования Django, за исключением того, что конфигурация по умолчанию отображает записи логов только при DEBUG=True. Django не регистрирует много сообщений уровня INFO. Однако с этой конфигурацией вы также можете установить переменную среды DJANGO_LOG_LEVEL=DEBUG, чтобы увидеть всю отладочную информацию логгирования Django, которая очень подробна, так как включает все запросы к базе данных.
Вы не обязаны регистрировать данные в консоли. Вот конфигурация, которая записывает всю информацию логгирования из логгера с именем django в локальный файл:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
Если вы используете этот пример, обязательно измените путь к 'filename' на место, доступное для записи пользователем, который запускает приложение Django.
Наконец, вот пример довольно сложной конфигурации логгирования:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': {
'special': {
'()': 'project.logging.SpecialFilter',
'foo': 'bar',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
}
},
'loggers': {
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
Эта конфигурация логгирования выполняет следующие действия:
- Определяет конфигурацию как «dictConfig version 1». На данный момент это единственная версия формата dictConfig.
-
Определяет два форматировщика:
-
simple, который выводит имя уровня лога (например,DEBUG) и сообщение лога.Строка
format— это обычная строка форматирования Python, описывающая детали, которые должны выводиться в каждой строке лога. Полный список деталей, которые можно вывести, можно найти в Объектах форматирования. -
verbose, который выводит имя уровня лога, сообщение лога, а также время, процесс, поток и модуль, которые сгенерировали сообщение лога.
-
-
Определяет два фильтра:
-
project.logging.SpecialFilter, используя псевдонимspecial. Если этому фильтру требуются дополнительные аргументы, они могут быть предоставлены как дополнительные ключи в словаре конфигурации фильтра. В этом случае аргументуfooбудет присвоено значениеbarпри создании экземпляраSpecialFilter. -
django.utils.log.RequireDebugTrue, который пропускает записи, когдаDEBUGравноTrue.
-
-
Определяет два обработчика:
-
console,StreamHandler, который выводит любое сообщение уровняINFO(или выше) вsys.stderr. Этот обработчик использует формат выводаsimple. -
mail_admins,AdminEmailHandler, который отправляет электронное письмо со всеми сообщениями уровняERROR(или выше) на адрес сайтаADMINS. Этот обработчик использует фильтрspecial.
-
-
Настраивает три логгера:
-
django, который передает все сообщения обработчикуconsole. -
django.request, который передает все сообщенияERRORобработчикуmail_admins. Кроме того, этот логгер помечен как не передающий сообщения. Это означает, что сообщения логов, записанные вdjango.request, не будут обрабатываться логгеромdjango. -
myproject.custom, который передает все сообщения уровняINFOили выше, которые также проходят фильтрspecial, в два обработчика —console, иmail_admins. Это означает, что все сообщения уровняINFO(или выше) будут выводиться в консоль; сообщения уровняERRORиCRITICALтакже будут выводиться по электронной почте.
-
Настройка логгирования
Если вы не хотите использовать формат dictConfig Python для настройки своего логгера, вы можете указать свою собственную схему конфигурации.
Настройка LOGGING_CONFIG определяет вызываемый объект, который будет использоваться для настройки логгеров Django. По умолчанию он указывает на функцию Python logging.config.dictConfig(). Однако если вы хотите использовать другой процесс конфигурации, вы можете использовать любой другой вызываемый объект, который принимает один аргумент. Содержимое LOGGING будет предоставлено в качестве значения этого аргумента при настройке логгирования.
Отключение конфигурации логгирования
Если вы не хотите настраивать логгирование вообще (или хотите настроить его вручную), вы можете установить LOGGING_CONFIG на значение None. Это отключит процесс конфигурации для стандартного логгирования Django.
Установка LOGGING_CONFIG на значение None означает только отключение автоматического процесса конфигурации, а не логгирования самого по себе. Если вы отключите процесс конфигурации, Django все равно будет выполнять вызовы логгирования, используя поведение по умолчанию.
Вот пример, который отключает конфигурацию логгирования Django и затем настраивает логгирование вручную:
LOGGING_CONFIG = None import logging.config logging.config.dictConfig(...)
Обратите внимание, что процесс конфигурации по умолчанию вызывает LOGGING_CONFIG только один раз после полной загрузки настроек. В отличие от этого, ручная настройка логгирования в файле настроек загрузит вашу конфигурацию логгирования сразу. Таким образом, ваша конфигурация логгирования должна появляться после любых настроек, от которых она зависит.
Расширения логгирования Django
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.1/topics/logging/