Ведение журнала¶
Краткое руководство по ведению журнала¶
Django использует встроенный модуль Python logging
для ведения системного журнала. Использование этого модуля подробно обсуждается в собственной документации Python. Однако, если вы никогда не использовали фреймворк ведения журналов Python (или даже если использовали), вот краткое руководство.
Состав игроков¶
Конфигурация ведения журнала Python состоит из четырех частей:
Логеры¶
Логер - это точка входа в систему регистрации. Каждый логер - это именованная корзина, в которую можно записывать сообщения для обработки.
Логер настроен на уровень журнала. Этот уровень журнала описывает серьезность сообщений, обрабатываемых логером. Python определяет следующие уровни журнала:
DEBUG
: системная информация низкого уровня для отладки.INFO
: Общая информация о системеWARNING
: Информация, описывающая возникшую незначительную проблему.ERROR
: Информация, описывающая возникшую серьезную проблему.CRITICAL
: Информация, описывающая возникшую критическую проблему.
Каждое сообщение, которое записывается в регистратор, является записью журнала. Каждая запись журнала также имеет уровень журнала, указывающий на серьезность данного конкретного сообщения. Запись журнала может также содержать полезные метаданные, описывающие регистрируемое событие. Это могут быть такие детали, как трассировка стека или код ошибки.
Когда сообщение поступает в регистратор, уровень журнала сообщения сравнивается с уровнем журнала регистратора. Если уровень журнала сообщения соответствует уровню журнала самого регистратора или превышает его, сообщение будет обработано. Если нет, то сообщение будет проигнорировано.
Как только регистратор определил, что сообщение должно быть обработано, оно передается в Handler.
Обработчики¶
Обработчик - это механизм, определяющий, что происходит с каждым сообщением в регистраторе. Он описывает определенное поведение регистратора, например, запись сообщения на экран, в файл или в сетевой сокет.
Как и регистраторы, обработчики также имеют уровень журнала. Если уровень записи журнала не соответствует уровню обработчика или превышает его, обработчик проигнорирует сообщение.
У регистратора может быть несколько обработчиков, и каждый обработчик может иметь свой уровень регистрации. Таким образом, можно обеспечить различные формы уведомления в зависимости от важности сообщения. Например, можно установить один обработчик, который пересылает сообщения ERROR
и CRITICAL
в службу подкачки, а второй обработчик записывает все сообщения (включая ERROR
и CRITICAL
) в файл для последующего анализа.
Фильтры¶
Фильтр используется для обеспечения дополнительного контроля над тем, какие записи журнала передаются от регистратора к обработчику.
По умолчанию обрабатывается любое сообщение журнала, отвечающее требованиям уровня журнала. Однако, установив фильтр, вы можете задать дополнительные критерии для процесса протоколирования. Например, вы можете установить фильтр, который позволяет выдавать только сообщения ERROR
из определенного источника.
Фильтры также можно использовать для изменения записи журнала перед ее выдачей. Например, вы можете написать фильтр, который понижает уровень ERROR
записей журнала до WARNING
записей, если выполняется определенный набор критериев.
Фильтры можно устанавливать на регистраторы или на обработчики; несколько фильтров можно использовать в цепочке для выполнения нескольких действий фильтрации.
Форматировщики¶
В конечном итоге запись журнала должна быть представлена в виде текста. Форматировщики описывают точный формат этого текста. Обычно форматтер состоит из строки форматирования Python, содержащей LogRecord attributes; однако вы также можете написать собственные форматтеры для реализации специфического поведения форматирования.
Использование протоколирования¶
После того как вы настроили регистраторы, обработчики, фильтры и форматеры, вам нужно поместить вызовы регистрации в ваш код. Использование фреймворка протоколирования работает следующим образом:
# 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 format.
In order to configure logging, you use LOGGING
to define a
dictionary of logging settings. These settings describe the loggers,
handlers, filters and formatters that you want in your logging setup,
and the log levels and other properties that you want those components
to have.
По умолчанию установка LOGGING
объединяется с Django’s default logging configuration по следующей схеме.
Если ключ disable_existing_loggers
в директ-конфиге LOGGING
установлен в True
(что является dictConfig
по умолчанию, если ключ отсутствует), то все регистраторы из конфигурации по умолчанию будут отключены. Отключенные регистраторы - это не то же самое, что удаленные; регистратор будет по-прежнему существовать, но будет молча отбрасывать все, что записывается в него, даже не передавая записи в родительский регистратор. Таким образом, вы должны быть очень осторожны, используя 'disable_existing_loggers': True
; вероятно, это не то, что вам нужно. Вместо этого вы можете установить disable_existing_loggers
в False
и переопределить некоторые или все логгеры по умолчанию; или вы можете установить LOGGING_CONFIG
в None
и handle logging config yourself.
Логирование настраивается как часть общей функции Django setup()
. Поэтому вы можете быть уверены, что логгеры всегда готовы к использованию в коде вашего проекта.
Примеры:¶
Полная документация по dictConfig format является лучшим источником информации о словарях конфигурации журнала. Однако, чтобы дать вам представление о том, что возможно, приведем несколько примеров.
Для начала, вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
}
Это настраивает родительский регистратор root
на отправку сообщений с уровнем WARNING
и выше в обработчик консоли. Изменив уровень до INFO
или DEBUG
, можно вывести больше сообщений. Это может быть полезно во время разработки.
Далее мы можем добавить более тонкое протоколирование. Вот пример того, как заставить систему протоколирования выводить больше сообщений только из именованного регистратора django:
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
'propagate': False,
},
},
}
По умолчанию этот конфиг отправляет сообщения от логгера django
уровня INFO
или выше на консоль. Это тот же уровень, что и стандартная конфигурация логирования Django, за исключением того, что стандартная конфигурация отображает записи журнала только при уровне DEBUG=True
. Django не записывает много сообщений уровня INFO
. Однако с этой конфигурацией вы также можете установить переменную окружения DJANGO_LOG_LEVEL=DEBUG
для просмотра всех отладочных логов Django, которые очень подробны, так как включают все запросы к базе данных.
Вам не обязательно вести журнал в консоль. Вот конфигурация, которая записывает все логи из django именованного логгера в локальный файл:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
Если вы используете этот пример, не забудьте изменить путь 'filename'
на место, доступное для записи пользователю, который запускает приложение Django.
Наконец, вот пример довольно сложной настройки протоколирования:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': {
'special': {
'()': 'project.logging.SpecialFilter',
'foo': 'bar',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
}
},
'loggers': {
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
Эта конфигурация протоколирования выполняет следующие действия:
Идентифицирует конфигурацию как имеющую формат „dictConfig версии 1“. В настоящее время это единственная версия формата dictConfig.
Определяет два форматера:
simple
, который выводит имя уровня журнала (например,DEBUG
) и сообщение журнала.Строка
format
- это обычная строка форматирования Python, описывающая детали, которые должны быть выведены в каждой строке журнала. Полный список деталей, которые могут быть выведены, можно найти в Formatter Objects.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
сообщения также будут выведены по электронной почте.
Пользовательская конфигурация протоколирования¶
Если вы не хотите использовать формат Python dictConfig для настройки регистратора, вы можете задать свою собственную схему конфигурации.
Параметр LOGGING_CONFIG
определяет вызываемую функцию, которая будет использоваться для настройки логгеров Django. По умолчанию он указывает на функцию Python logging.config.dictConfig()
. Однако, если вы хотите использовать другой процесс конфигурации, вы можете использовать любую другую вызываемую функцию, принимающую один аргумент. Содержимое LOGGING
будет предоставлено в качестве значения этого аргумента при настройке логирования.
Отключение конфигурации ведения журнала¶
Если вы не хотите настраивать ведение журнала вообще (или хотите вручную настроить ведение журнала, используя свой собственный подход), вы можете установить LOGGING_CONFIG
на None
. Это отключит процесс конфигурирования для Django’s default logging.
Установка 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
s, существует свой поджурнал. Уровень события журнала зависит от того, как обрабатывается исключение. Большинство случаев регистрируется как предупреждение, в то время как любая 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 failures.
django.db.backends.schema
¶
Регистрирует SQL-запросы, которые выполняются во время изменения схемы базы данных командой migrations framework. Обратите внимание, что он не будет регистрировать запросы, выполняемые RunPython
. Сообщения этого логгера имеют params
и sql
в дополнительном контексте (но, в отличие от django.db.backends
, не продолжительность). Значения имеют тот же смысл, который объясняется в django.db.backends.
Обработчики¶
Django предоставляет один обработчик журналов в дополнение к тем, которые предоставляются модулем протоколирования Python.
-
class
AdminEmailHandler
(include_html=False, email_backend=None, reporter_class=None)[исходный код]¶ Этот обработчик отправляет письмо на сайт
ADMINS
для каждого полученного им сообщения журнала.Если запись журнала содержит атрибут
request
, в письмо будет включена полная информация о запросе. Тема письма будет включать фразу «internal IP», если IP-адрес клиента находится в настройкахINTERNAL_IPS
; если нет, она будет включать «EXTERNAL 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, чтобы получить лучшее из двух миров - богатую информацию полного отслеживания и безопасность не отправки информации по электронной почте. Вы также можете явно указать определенную конфиденциальную информацию, которая будет отфильтрована из отчетов об ошибках - узнайте больше о Filtering error reports.
Установив аргумент
email_backend
вAdminEmailHandler
, можно переопределить email backend, используемый обработчиком, следующим образом:'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' } },
-
send_mail
(subject, message, *args, **kwargs)[исходный код]¶ Отправляет электронные письма пользователям-администраторам. Чтобы настроить это поведение, вы можете использовать подкласс класса
AdminEmailHandler
и переопределить этот метод.
-
Фильтры¶
Django предоставляет некоторые фильтры журналов в дополнение к тем, которые предоставляет модуль протоколирования Python.
-
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
прикрепляются к корневому логгеру для обеспечения описанного выше поведения.
Смотрите также Configuring logging, чтобы узнать, как вы можете дополнить или заменить эту конфигурацию протоколирования по умолчанию, определенную в django/utils/log.py.