Логгирование¶
A quick logging primer¶
Django uses Python’s builtin logging module to perform system logging.
The usage of this module is discussed in detail in Python’s own documentation.
However, if you’ve never used Python’s logging framework (or even if you have),
here’s a quick primer.
Действующие лица¶
Конфигурация логгирования в Python состоит из четырех частей:
Loggers(Логгеры)¶
A logger is the entry point into the logging system. Each logger is a named bucket to which messages can be written for processing.
У каждого логгера есть уровень логгирования (log level). Уровень логгирования указывает важность принимаемых сообщений. Python определяет следующие уровни логгирования:
DEBUG: Низкий уровень логгирования системной информации для последующего использования в отладкеINFO: Общая системная информацияWARNING: Информация о мелких проблемах возникших при работе приложенияERROR: Информация об ошибках возникших при работе приложенияCRITICAL: Информация о критических ошибках
Каждое сообщение записанное в логгер называется Log Record(Запись). Каждая запись содержит уровень логгирования, который указывает важность сообщения. Сообщение также может содержать метаданные, которые описывают произошедшее событие. Метаданные могут содержать код ошибки или отладочную информацию.
Когда сообщение передается в логгер, уровень логгирования сообщения сравнивается с уровнем логгирования логгера. Если уровень логгирования сообщения равен или выше уровню логгирования логгера, сообщение будет обработано, иначе - проигнорировано.
После того, как логгер принял сообщение на обработку, оно передается в Handler(Обработчик).
Handlers(Обработчики)¶
The handler is the engine that determines what happens to each message in a logger. It describes a particular logging behavior, such as writing a message to the screen, to a file, or to a network socket.
Как и логгеры, обработчики имеют уровень логгирования. Если уровень логгирования сообщения ниже уровня логгирования обработчика, сообщение будет проигнорировано.
Логгер может содержать несколько обработчиков, которые могут иметь различный уровень логгирования. Это позволяет обрабатывать сообщения в соответствии с их уровнем важности. Например, вы можете установить обработчик для ERROR и CRITICAL сообщений, который будет отправлять через какой-то сервер сообщений, и в то же время обработчик записывающий все сообщения (включая ERROR и CRITICAL) в файл.
Фильтры¶
A filter is used to provide additional control over which log records are passed from logger to handler.
По умолчанию все сообщения, прошедшие проверку уровня логгирования, будут переданы в обработчик. Добавив фильтры вы можете определить дополнительные правила проверки при обработке сообщений. Например, вы можете добавить фильтр, который позволяет обрабатывать ERROR сообщения отправленные определенным источником.
Фильтры также могу изменить сообщение. Например, вы можете создать фильтр, который изменяет уровень логгирования определенных сообщения с ERROR на WARNING.
Фильтр могут быть добавлены к логгеру или обработчику, можно использовать несколько фильтров.
Formatters(Форматер)¶
Ultimately, a log record needs to be rendered as text. Formatters describe the exact format of that text. A formatter usually consists of a Python formatting string containing LogRecord attributes; however, you can also write custom formatters to implement specific formatting behavior.
Using logging¶
Once you have configured your loggers, handlers, filters and formatters, you need to place logging calls into your code. Using the logging framework works like this:
# 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!')
And that’s it! Every time the bad_mojo condition is activated, an
error log record will be written.
Naming loggers¶
The call to logging.getLogger() obtains (creating, if
necessary) an instance of a logger. The logger instance is identified
by a name. This name is used to identify the logger for configuration
purposes.
By convention, the logger name is usually __name__, the name of
the Python module that contains the logger. This allows you to filter
and handle logging calls on a per-module basis. However, if you have
some other way of organizing your logging messages, you can provide
any dot-separated name to identify your logger:
# Get an instance of a specific named logger
logger = logging.getLogger('project.interesting.stuff')
The dotted paths of logger names define a hierarchy. The
project.interesting logger is considered to be a parent of the
project.interesting.stuff logger; the project logger
is a parent of the project.interesting logger.
Why is the hierarchy important? Well, because loggers can be set to
propagate their logging calls to their parents. In this way, you can
define a single set of handlers at the root of a logger tree, and
capture all logging calls in the subtree of loggers. A logger defined
in the project namespace will catch all logging messages issued on
the project.interesting and project.interesting.stuff loggers.
This propagation can be controlled on a per-logger basis. If you don’t want a particular logger to propagate to its parents, you can turn off this behavior.
Making logging calls¶
The logger instance contains an entry method for each of the default log levels:
logger.debug()logger.info()logger.warning()logger.error()logger.critical()
There are two other logging calls available:
logger.log(): Manually emits a logging message with a specific log level.logger.exception(): Creates anERRORlevel logging message wrapping the current exception stack frame.
Настройка логгирования¶
It isn’t enough to just put logging calls into your code. You also need to configure the loggers, handlers, filters, and formatters to ensure you can use the logging output.
Библиотека логгирования 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 лучше всего описывает формат словаря конфигурации журналирования. Однако, чтобы показать вам её возможности, мы приведем несколько примеров.
Для начала вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:
settings.py¶import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
}
Это настраивает родительский корневой регистратор для отправки сообщений с уровнем WARNING и выше обработчику консоли. Установив уровень «INFO» или «DEBUG», вы можете отображать больше сообщений. Это может быть полезно во время разработки.
Далее мы можем добавить более детальное ведение журнала. Вот пример того, как заставить систему журналирования печатать больше сообщений только из django с именем logger:
settings.py¶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 с именем logger в локальный файл:
settings.py¶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,
},
},
}
При использовании этого примера убедитесь, что пользователь, от которого запускается Django приложение, имеет права на запись файла, указанного в 'filename'.
Наконец, здесь показан пример достаточно сложной конфигурации журналирования:
settings.py¶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, описывающая детали, которые будут присутствовать в каждой строке журнала. Полный список переменных для форматирования вы можете найти в документации.
verbose, выведет уровень логгирования, сообщение, время, название процесса, потока и модуля, который создал сообщение.
Определяет два фильтра:
project.logging.SpecialFilterс названиемspecial. Если конструктор фильтра требует наличия дополнительных аргументов, вы можете указать их в словаре настройки фильтра. В этом случае будет передан аргументfooсо значениемbarпри создании экземпляраSpecialFilter.django.utils.log.RequireDebugTrue– пропускает записи только в случае когда параметр конфигурацииDEBUGравен True.
Определяет два обработчика:
console, StreamHandler, который перенаправляет все сообщения уровняDEBUG(и выше) в stderr. Этот обработчик использует формат simple.mail_admins, anAdminEmailHandler, which emails anyERROR(or higher) message to the siteADMINS. This handler uses thespecialfilter.
Настраивает три логгера:
django, который перенаправляет все сообщения в обработчикconsole.django.request, который передает все сообщения уровняERRORв обработчикmail_admins. Также указывается, что логгер не должен передавать сообщения родительским логгерам. Это означает что сообщения переданные вdjango.requestне будут обрабатываться логгеромdjango.myproject.custom, который передает все сообщения уровняINFOи выше прошедшие фильтрspecialв два обработчика –consoleиmail_admins. Это означает что все сообщения уровняINFO(или выше) будут отправлены в консоль, сообщенияERRORиCRITICALбудут отосланы через e-mail.
Собственная конфигурация логгирования¶
Если вы не хотите использовать формат dictConfig для настройки логгирования, вы можете определить собственный формат.
Параметр конфигурации LOGGING_CONFIG определяет функцию, которая настраивает логгирование в Django. По умолчанию, параметр указывает на logging.dictConfig(). Однако, если вы хотите переопределить настройку логгирования, укажите функцию, которая принимает один аргумент. Содержимое LOGGING будет передано в функцию при настройке логгирования.
Отключение настройки логгирования¶
Если вы вообще не хотите настраивать ведение журнала (или хотите настроить ведение журнала вручную, используя свой собственный подход), вы можете установить для LOGGING_CONFIG значение None. Это отключит процесс настройки Django ведения журнала по умолчанию.
Установка LOGGING_CONFIG в None только означает, что процесс автоматической настройки отключен, это не влияет на само журналирование. Если вы отключите процесс конфигурации, Django все равно будет вызывать методы журналирования, используя поведение логгеров, настроенное по умолчанию.
Вот пример, который отключает конфигурацию ведения журнала Django, а затем настраивает ведение журнала вручную:
settings.py¶LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
Обратите внимание, что процесс настройки по умолчанию вызывает LOGGING_CONFIG только после полной загрузки настроек. Напротив, настройка ведения журнала вручную в файле настроек приведет к немедленной загрузке конфигурации ведения журнала. Таким образом, ваша конфигурация ведения журнала должна появиться после любых настроек, от которых она зависит.
Django’s logging extensions¶
Django provides a number of utilities to handle the unique requirements of logging in Web server environment.
Loggers(Логгеры)¶
Django provides several built-in loggers.
django¶
The catch-all logger for messages in the django hierarchy. No messages are
posted using this name but instead using one of the loggers below.
django.request¶
Log messages related to the handling of requests. 5XX responses are
raised as ERROR messages; 4XX responses are raised as WARNING
messages. Requests that are logged to the django.security logger aren’t
logged to django.request.
Messages to this logger have the following extra context:
status_code: The HTTP response code associated with the request.request: The request object that generated the logging message.
django.server¶
Log messages related to the handling of requests received by the server invoked
by the runserver command. HTTP 5XX responses are logged as ERROR
messages, 4XX responses are logged as WARNING messages, and everything else
is logged as INFO.
Messages to this logger have the following extra context:
status_code: The HTTP response code associated with the request.request: The request object that generated the logging message.
django.template¶
Log messages related to the rendering of templates.
Missing context variables are logged as
DEBUGmessages.
django.db.backends¶
Messages relating to the interaction of code with the database. For example,
every application-level SQL statement executed by a request is logged at the
DEBUG level to this logger.
Messages to this logger have the following extra context:
duration: The time taken to execute the SQL statement.sql: The SQL statement that was executed.params: The parameters that were used in the SQL call.
For performance reasons, SQL logging is only enabled when
settings.DEBUG is set to True, regardless of the logging
level or handlers that are installed.
This logging does not include framework-level initialization (e.g.
SET TIMEZONE) or transaction management queries (e.g. BEGIN,
COMMIT, and ROLLBACK). Turn on query logging in your database if you
wish to view all database queries.
django.security.*¶
The security loggers will receive messages on any occurrence of
SuspiciousOperation and other security-related
errors. There is a sub-logger for each subtype of security error, including all
SuspiciousOperations. The level of the log event depends on where the
exception is handled. Most occurrences are logged as a warning, while
any SuspiciousOperation that reaches the WSGI handler will be logged as an
error. For example, when an HTTP Host header is included in a request from
a client that does not match ALLOWED_HOSTS, Django will return a 400
response, and an error message will be logged to the
django.security.DisallowedHost logger.
These log events will reach the django logger by default, which mails error
events to admins when DEBUG=False. Requests resulting in a 400 response due
to a SuspiciousOperation will not be logged to the django.request
logger, but only to the django.security logger.
To silence a particular type of SuspiciousOperation, you can override that
specific logger following this example:
'handlers': {
'null': {
'class': 'logging.NullHandler',
},
},
'loggers': {
'django.security.DisallowedHost': {
'handlers': ['null'],
'propagate': False,
},
},
Other django.security loggers not based on SuspiciousOperation are:
django.security.csrf: For CSRF failures.
django.db.backends.schema¶
Logs the SQL queries that are executed during schema changes to the database by
the migrations framework. Note that it won’t log the
queries executed by RunPython.
Messages to this logger have params and sql in their extra context (but
unlike django.db.backends, not duration). The values have the same meaning
as explained in django.db.backends.
Handlers(Обработчики)¶
Django provides one log handler in addition to those provided by the Python logging module.
- class AdminEmailHandler(include_html=False, email_backend=None, reporter_class=None)¶
This handler sends an email to the site
ADMINSfor each log message it receives.If the log record contains a
requestattribute, the full details of the request will be included in the email. The email subject will include the phrase «internal IP» if the client’s IP address is in theINTERNAL_IPSsetting; if not, it will include «EXTERNAL IP».If the log record contains stack trace information, that stack trace will be included in the email.
The
include_htmlargument ofAdminEmailHandleris used to control whether the traceback email includes an HTML attachment containing the full content of the debug Web page that would have been produced ifDEBUGwereTrue. To set this value in your configuration, include it in the handler definition fordjango.utils.log.AdminEmailHandler, like this:'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'include_html': True, } },
Note that this HTML version of the email contains a full traceback, with names and values of local variables at each level of the stack, plus the values of your Django settings. This information is potentially very sensitive, and you may not want to send it over email. Consider using something such as Sentry to get the best of both worlds – the rich information of full tracebacks plus the security of not sending the information over email. You may also explicitly designate certain sensitive information to be filtered out of error reports – learn more on Filtering error reports.
By setting the
email_backendargument ofAdminEmailHandler, the email backend that is being used by the handler can be overridden, like this:'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'email_backend': 'django.core.mail.backends.filebased.EmailBackend', } },
By default, an instance of the email backend specified in
EMAIL_BACKENDwill be used.The
reporter_classargument ofAdminEmailHandlerallows providing andjango.views.debug.ExceptionReportersubclass to customize the traceback text sent in the email body. You provide a string import path to the class you wish to use, like this:'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)¶
Sends emails to admin users. To customize this behavior, you can subclass the
AdminEmailHandlerclass and override this method.
Фильтры¶
Django provides some log filters in addition to those provided by the Python logging module.
- class CallbackFilter(callback)¶
This filter accepts a callback function (which should accept a single argument, the record to be logged), and calls it for each record that passes through the filter. Handling of that record will not proceed if the callback returns False.
For instance, to filter out
UnreadablePostError(raised when a user cancels an upload) from the admin emails, you would create a filter function: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
and then add it to your logging config:
'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¶
This filter will only pass on records when settings.DEBUG is False.
This filter is used as follows in the default
LOGGINGconfiguration to ensure that theAdminEmailHandleronly sends error emails to admins whenDEBUGisFalse:'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse', } }, 'handlers': { 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' } },
- class RequireDebugTrue¶
This filter is similar to
RequireDebugFalse, except that records are passed only whenDEBUGisTrue.
Django’s default logging configuration¶
By default, Django configures the following logging:
When DEBUG is True:
The
djangologger sends messages in thedjangohierarchy (exceptdjango.server) at theINFOlevel or higher to the console.
When DEBUG is False:
The
djangologger sends messages in thedjangohierarchy (exceptdjango.server) withERRORorCRITICALlevel toAdminEmailHandler.
Independent of the value of DEBUG:
The django.server logger sends messages at the
INFOlevel or higher to the console.
All loggers except django.server propagate logging to their
parents, up to the root django logger. The console and mail_admins
handlers are attached to the root logger to provide the behavior described
above.
See also Configuring logging to learn how you can complement or replace this default logging configuration defined in django/utils/log.py.