Spec-Zone.ru › Django 3.2

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

Быстрый обзор ведения журнала

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

Актеры

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

  • Логгеры
  • Обработчики
  • Фильтры
  • Форматировщики

Логгеры

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

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

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

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

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

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

Обработчики

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

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

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

Фильтры

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

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

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

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

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

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

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

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

# import the logging library
import logging

# Get an instance of a logger
logger = logging.getLogger(__name__)

def my_view(request, arg1, arg):
    ...
    if bad_mojo:
        # Log an error message
        logger.error('Something went wrong!')

И на этом всё! Каждый раз, когда условие bad_mojo активируется, запись журнала об ошибке будет записана.

Именование логгеров

Вызов logging.getLogger() получает (создаёт, если необходимо) экземпляр логгера. Экземпляр логгера идентифицируется по имени. Это имя используется для идентификации логгера в целях настройки.

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

# Get an instance of a specific named logger
logger = logging.getLogger('project.interesting.stuff')

Точечные пути имен логгеров определяют иерархию. Логгер project.interesting считается родительским по отношению к логгеры project.interesting.stuff; логгер project является родительским по отношению к логгеры project.interesting.

Зачем важна иерархия? Потому что логгеры могут быть настроены на распространение своих вызовов ведения журнала до своих родителей. Таким образом, вы можете определить один набор обработчиков в корне дерева логгеров и захватить все вызовы ведения журнала в поддереве логгеров. Логгер, определённый в пространстве имён project, перехватит все сообщения ведения журнала, выпущенные логгерами project.interesting и project.interesting.stuff.

Это распространение можно контролировать на уровне каждого логгера. Если вы не хотите, чтобы определённый логгер распространялся до своих родителей, вы можете отключить это поведение.

Выполнение вызовов ведения журнала

Экземпляр логгера содержит метод входа для каждого из стандартных уровней журнала:

  • logger.debug()
  • logger.info()
  • logger.warning()
  • logger.error()
  • logger.critical()

Доступны два других вызова ведения журнала:

  • logger.log(): Вручную отправляет сообщение ведения журнала с определённым уровнем журнала.
  • logger.exception(): Создает сообщение журнала уровня ERROR, обертывая текущую рамку стека исключений.

Настройка ведения журнала

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

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

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

По умолчанию настройка LOGGING объединяется с стандартной конфигурацией ведения журнала Django с помощью следующего алгоритма.

Если ключ disable_existing_loggers в словаре LOGGING установлен в значение True (что является значением по умолчанию, если ключ отсутствует), все логгеры из стандартной конфигурации будут отключены. Отключенные логгеры не такие же, как удалённые; логгер по-прежнему будет существовать, но молча будет отбрасывать всё, что ему регистрируется, не перенаправляя даже записи родительскому логгеры. Поэтому будьте очень осторожны, используя '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',
    },
}

Эта конфигурация настраивает родительский логгер 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 version 1». В настоящее время это единственный формат версии dictConfig.
  • Определяет два форматера:

    • simple, который выводит имя уровня лога (например, DEBUG) и сообщение лога.

      Строка format — это обычная строка форматирования Python, описывающая детали, которые должны быть выведены в каждой строке лога. Полный список деталей, которые можно вывести, можно найти в Объектах форматера.

    • verbose, который выводит имя уровня лога, сообщение лога, а также время, процесс, поток и модуль, которые сгенерировали сообщение лога.
  • Определяет два фильтра:

    • project.logging.SpecialFilter, используя псевдоним special. Если этому фильтру требуются дополнительные аргументы, их можно указать как дополнительные ключи в словаре конфигурации фильтра. В этом случае аргументу foo будет присвоено значение bar при создании объекта SpecialFilter.
    • django.utils.log.RequireDebugTrue, который пропускает записи, когда DEBUG равно True.
  • Определяет два обработчика:

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

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

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

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

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

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

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

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

Вот пример, который отключает конфигурацию логирования Django и затем настраивает логирование вручную:

settings.py
LOGGING_CONFIG = None

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

Обратите внимание, что процесс конфигурации по умолчанию вызывает LOGGING_CONFIG только один раз после полной загрузки настроек. В отличие от этого, ручная настройка логирования в файле настроек загрузит вашу конфигурацию логирования немедленно. Поэтому ваша конфигурация логирования должна находиться *после* любых настроек, от которых она зависит.

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

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

Логгеры

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

django

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

django.request

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

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

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

django.server

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

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

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

django.template

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

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

django.db.backends

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

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

  • duration: Время выполнения SQL-предложения.
  • sql: Само SQL-предложение.
  • params: Параметры, используемые в SQL-вызове.

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

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

django.security.*

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

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

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

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

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

  • django.security.csrf: Для отказанных запросов CSRF.

django.db.backends.schema

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

Обработчики

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

class AdminEmailHandler(include_html=False, email_backend=None, reporter_class=None)

Этот обработчик отправляет электронное письмо на сайт ADMINS для каждого сообщения журнала.

Если запись журнала содержит атрибут request, полные данные запроса будут включены в письмо. Тема письма будет содержать фразу «внутренний IP», если IP-адрес клиента находится в настройке INTERNAL_IPS; в противном случае она будет содержать «ВНЕШНИЙ IP».

Если запись журнала содержит информацию о стеке вызовов, этот стек вызовов будет включён в письмо.

Аргумент include_html метода AdminEmailHandler используется для управления включением в письмо отладки HTML-приложения, содержащего полное содержимое страницы отладки веб-сайта, которая была бы создана, если бы DEBUG было True. Чтобы установить это значение в вашей конфигурации, включите его в определение обработчика для django.utils.log.AdminEmailHandler, как в этом примере:

'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler',
        'include_html': True,
    }
},

Обратите внимание, что этот HTML-вариант письма содержит полный стек вызовов с именами и значениями локальных переменных на каждом уровне стека, а также значениями настроек Django. Эта информация потенциально очень чувствительна, и вы можете не захотеть отправлять её по электронной почте. Рассмотрите использование такого сервиса, как Sentry, чтобы получить лучшее из обоих миров — богатую информацию полных стеков вызовов плюс безопасность не отправлять информацию по электронной почте. Вы также можете явно обозначить определённую чувствительную информацию для фильтрации из отчётов об ошибках — узнайте больше на Фильтрование отчётов об ошибках.

Устанавливая аргумент email_backend метода AdminEmailHandler, можно переопределить используемый 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 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.2/topics/logging/

Spec-Zone.ru

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