• 3.1
  • 5.0
  • 6.1
  • Версия документации: 3.2

Логгирование

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 an ERROR level 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, an AdminEmailHandler, which emails any ERROR (or higher) message to the site ADMINS. This handler uses the special filter.

  • Настраивает три логгера:

    • 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 DEBUG messages.

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.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 ADMINS for each log message it receives.

If the log record contains a request attribute, 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 the INTERNAL_IPS setting; 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_html argument of AdminEmailHandler is 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 if DEBUG were True. To set this value in your configuration, include it in the handler definition for django.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_backend argument of AdminEmailHandler, 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_BACKEND will be used.

The reporter_class argument of AdminEmailHandler allows providing an django.views.debug.ExceptionReporter subclass 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 AdminEmailHandler class 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 LOGGING configuration to ensure that the AdminEmailHandler only sends error emails to admins when DEBUG is 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

This filter is similar to RequireDebugFalse, except that records are passed only when DEBUG is True.

Django’s default logging configuration

By default, Django configures the following logging:

When DEBUG is True:

  • The django logger sends messages in the django hierarchy (except django.server) at the INFO level or higher to the console.

When DEBUG is False:

  • The django logger sends messages in the django hierarchy (except django.server) with ERROR or CRITICAL level to AdminEmailHandler.

Independent of the value of DEBUG:

  • The django.server logger sends messages at the INFO level 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.

Back to Top