Spec-Zone.ru › Django 3.0

Ведение журнала

Краткий обзор ведения журнала

Django использует встроенный модуль Python logging для выполнения системного ведения журнала. Подробное описание использования этого модуля можно найти в документации Python. Однако, если вы никогда не использовали фреймворк ведения журнала Python (или даже если использовали), вот краткий обзор.

Участники процесса

Настройка ведения журнала в Python состоит из четырёх частей:

  • Журнализаторы
  • Обработчики
  • Фильтры
  • Форматировщики

Журнализаторы

Журнализатор — это точка входа в систему ведения журнала. Каждый журнализатор — это именованный контейнер, в который можно записывать сообщения для обработки.

Журнализатор настраивается с уровнем журнала. Этот уровень журнала описывает степень важности сообщений, которые будет обрабатывать журнализатор. Python определяет следующие уровни журнала:

  • DEBUG: Информация низкого уровня о системе для отладки
  • INFO: Общая информация о системе
  • WARNING: Информация об обнаруженной незначительной проблеме.
  • ERROR: Информация об обнаруженной значительной проблеме.
  • CRITICAL: Информация об обнаруженной критической проблеме.

Каждое сообщение, отправляемое в журнализатор, — это Запись журнала. Каждая запись журнала также имеет уровень журнала, указывающий степень важности конкретного сообщения. Запись журнала также может содержать полезные метаданные, описывающие событие, которое регистрируется. Это может включать подробности, такие как трассировка стека или код ошибки.

Когда сообщение передаётся журнализатору, уровень журнала сообщения сравнивается с уровнем журнала самого журнализатора. Если уровень журнала сообщения соответствует или превышает уровень журнала самого журнализатора, сообщение будет подвергнуто дальнейшей обработке. В противном случае сообщение будет проигнорировано.

После того, как журнализатор определил, что сообщение необходимо обработать, оно передаётся Обработчику.

Обработчики

Обработчик — это механизм, определяющий, что происходит с каждым сообщением в журнализаторе. Он описывает конкретное поведение ведения журнала, такое как запись сообщения на экран, в файл или в сетевой сокет.

Как и журнализаторы, обработчики также имеют уровень журнала. Если уровень журнала записи журнала не соответствует или не превышает уровень обработчика, обработчик проигнорирует сообщение.

Журнализатор может иметь несколько обработчиков, и каждый обработчик может иметь различный уровень журнала. Таким образом, можно предоставить различные формы уведомлений в зависимости от важности сообщения. Например, можно установить один обработчик, который пересылает сообщения ERROR и CRITICAL в службу оповещения, а второй обработчик записывает все сообщения (включая сообщения ERROR и CRITICAL) в файл для последующего анализа.

Фильтры

Фильтр используется для дополнительного управления тем, какие записи журнала передаются из журнализатора в обработчик.

По умолчанию любое сообщение журнала, которое соответствует требованиям уровня журнала, будет обработано. Однако, установив фильтр, можно добавить дополнительные критерии к процессу ведения журнала. Например, можно установить фильтр, который разрешает передавать только сообщения ERROR из определённого источника.

Фильтры также могут использоваться для изменения записи журнала перед её отправкой. Например, можно написать фильтр, который понижает уровень ERROR записей журнала до WARNING записей, если выполняются определённые критерии.

Фильтры можно устанавливать на журнализаторы или на обработчики; можно использовать несколько фильтров в цепочке для выполнения нескольких фильтрующих действий.

Форматировщики

В конечном итоге запись журнала должна быть представлена в виде текста. Форматировщики описывают точный формат этого текста. Форматировщик обычно состоит из строки форматирования Python, содержащей атрибуты записи журнала; однако вы также можете создавать собственные форматировщики для реализации специфического поведения форматирования.

Использование ведения журнала

После настройки журнализаторов, обработчиков, фильтров и форматировщиков необходимо добавить вызовы ведения журнала в свой код. Работа с фреймворком ведения журнала происходит следующим образом:

# 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 format является лучшим источником информации о словарях конфигурации ведения журнала. Однако, чтобы дать вам представление о возможностях, вот несколько примеров.

Для начала, вот небольшая конфигурация, которая позволит отобразить все сообщения журнала в консоли:

settings.py
import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'WARNING',
    },
}

Эта конфигурация настраивает родительский журнализатор root на отправку сообщений уровня WARNING и выше в обработчик консоли. Изменив уровень на INFO или DEBUG, вы можете отобразить больше сообщений. Это может быть полезно при разработке.

Далее, мы можем добавить более детальное ведение журнала. Вот пример того, как сделать систему ведения журнала отображать больше сообщений только из журнализатора с именем django:

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 в локальный файл:

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,
        },
    },
}

Если вы используете этот пример, убедитесь, что изменили путь 'filename' на расположение, доступное для записи пользователю, выполняющему приложение Django.

Наконец, вот пример достаточно сложной настройки логгирования:

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, который выводит любое сообщение INFO (или выше) в sys.stderr. Этот обработчик использует формат вывода simple.
    • mail_admins, AdminEmailHandler, который отправляет электронное письмо со всеми сообщениями ERROR (или выше) администраторам сайта по адресу ADMINS. Этот обработчик использует фильтр special.
  • Настраивает три логгера:

    • django, который передает все сообщения обработчику console.
    • django.request, который передает все сообщения ERROR обработчику mail_admins Кроме того, этот логгер помечен как не распространяющий сообщения. Это означает, что сообщения логов, записанные в django.request не будут обработаны логгером django.
    • myproject.custom, который передает все сообщения уровня INFO или выше, которые также проходят через фильтр special, в два обработчика — console, и mail_admins . Это означает, что все сообщения уровня INFO (или выше) будут отображаться в консоли; сообщения уровня ERROR и CRITICAL также будут выведены по электронной почте.

Настройка логгирования

Если вы не хотите использовать формат dictConfig Python для настройки логгера, вы можете указать собственную схему конфигурации.

Настройка LOGGING_CONFIG определяет вызываемый объект, который будет использоваться для настройки логгеров Django. По умолчанию он указывает на функцию Python logging.config.dictConfig(). Однако, если вы хотите использовать другой процесс конфигурации, вы можете использовать любой другой вызываемый объект, принимающий один аргумент. Содержимое LOGGING будет передано в качестве значения этого аргумента при настройке логгирования.

Отключение конфигурации логгирования

Если вы не хотите настраивать логгирование вообще (или хотите настроить логгирование вручную с помощью собственного подхода), вы можете установить LOGGING_CONFIG на значение None. Это отключит процесс конфигурации для стандартной конфигурации логгирования Django. Вот пример, который отключает конфигурацию логгирования Django, а затем настраивает логгирование вручную:

settings.py
LOGGING_CONFIG = None

import logging.config
logging.config.dictConfig(...)

Установка LOGGING_CONFIG на значение None означает только то, что процесс автоматической конфигурации отключен, а не само логгирование. Если вы отключите процесс конфигурации, Django по-прежнему будет выполнять вызовы логгирования, используя поведение по умолчанию, которое определено.

Расширения логгирования Django

Django предоставляет ряд утилит для обработки особых требований к логгированию в среде веб-сервера.

Логгеры

Django предоставляет несколько встроенных логгеров.

django

Всеобщий логгер для сообщений в иерархии django. Сообщения не публикуются с этим именем, а вместо этого используются один из логгеров ниже.

django.request

Сообщения лога, связанные с обработкой запросов. 5XX ответы генерируются как сообщения ERROR; 4XX ответы генерируются как сообщения WARNING. Запросы, которые регистрируются в логгере django.security, не регистрируются в логгере django.request.

Сообщения в этом логгере содержат следующий дополнительный контекст:

  • status_code: Код HTTP-ответа, связанный с запросом.
  • request: Объект запроса, сгенерировавший сообщение лога.

django.server

Сообщения журнала, связанные с обработкой запросов, полученных сервером, вызванным командой runserver. HTTP-ответы 5XX регистрируются как сообщения ERROR, 4XX ответы регистрируются как сообщения WARNING, а все остальное — как INFO.

Сообщения в этом логгере содержат следующий дополнительный контекст:

  • status_code: Код HTTP-ответа, связанный с запросом.
  • request: Объект запроса, сгенерировавший сообщение лога.

django.template

Сообщения журнала, связанные с рендерингом шаблонов.

  • Отсутствующие переменные контекста регистрируются как сообщения DEBUG.

django.db.backends

Сообщения, связанные с взаимодействием кода с базой данных. Например, каждое SQL-утверждение уровня приложения, выполняемое запросом, регистрируется на уровне DEBUG в этом логгере.

Сообщения в этом логгере содержат следующий дополнительный контекст:

  • duration: Время выполнения SQL-запроса.
  • sql: SQL-запрос, который был выполнен.
  • params: Параметры, которые были использованы в вызове SQL.

По соображениям производительности, логгирование SQL включается только тогда, когда settings.DEBUG установлено в значение True, независимо от уровня логгирования или установленных обработчиков.

Это логгирование не включает инициализацию на уровне фреймворка (например, SET TIMEZONE) или запросы управления транзакциями (например, BEGIN, COMMIT, и ROLLBACK). Включите логгирование запросов в вашей базе данных, если хотите просмотреть все запросы к базе данных.

django.security.*

Логгеры безопасности получат сообщения при любом возникновении SuspiciousOperation и других ошибок, связанных с безопасностью. Существует подлоггер для каждого подтипа ошибки безопасности, включая все SuspiciousOperation. Уровень события лога зависит от места обработки исключения. Большинство случаев регистрируются как предупреждения, а любой SuspiciousOperation, достигающий обработчика WSGI, регистрируется как ошибка. Например, когда заголовок HTTP Host включается в запрос от клиента, который не соответствует ALLOWED_HOSTS, Django вернет ответ 400, и сообщение об ошибке будет записано в логгер django.security.DisallowedHost.

Эти события лога по умолчанию попадают в логгер django, который отправляет сообщения об ошибках администраторам, когда DEBUG=False. Запросы, которые приводят к ответу 400 из-за SuspiciousOperation, не будут записаны в логгер django.request, а только в логгер django.security.

Чтобы отключить определенный тип SuspiciousOperation, можно переопределить соответствующий логгер, следуя этому примеру:

'handlers': {
    'null': {
        'class': 'logging.NullHandler',
    },
},
'loggers': {
    'django.security.DisallowedHost': {
        'handlers': ['null'],
        'propagate': False,
    },
},

Другие логгеры django.security, не основанные на SuspiciousOperation:

  • django.security.csrf: Для ошибок CSRF.

django.db.backends.schema

Записывает SQL-запросы, выполняемые во время изменений схемы базы данных фреймворком миграций. Обратите внимание, что он не будет записывать запросы, выполняемые RunPython. Сообщения в этом логгере содержат params и sql в контексте дополнительных данных (но, в отличие от django.db.backends, без длительности). Значения имеют такое же значение, как и описано в django.db.backends.

Обработчики

Django предоставляет один обработчик логов дополнительно к тем, которые предоставляет модуль Python logging.

class AdminEmailHandler(include_html=False, email_backend=None, 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.0/topics/logging/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API