Spec-Zone.ru › Django 2.2

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

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

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

Во-первых, вот простая конфигурация, которая записывает весь лог из журнализатора django в локальный файл:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/path/to/django/debug.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

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

Во-вторых, вот пример того, как сделать так, чтобы система ведения журнала выводила лог Django в консоль. Это может быть полезно во время локального разработки.

По умолчанию эта конфигурация отправляет только сообщения уровня INFO или выше в консоль (такая же, как и стандартная конфигурация ведения журнала Django, за исключением того, что стандартная конфигурация показывает записи журнала только при DEBUG=True). Django не регистрирует много таких сообщений. Однако с этой конфигурацией вы также можете установить переменную среды DJANGO_LOG_LEVEL=DEBUG для просмотра всех отладочных журналов Django, которые очень подробны, так как включают все запросы к базе данных:

import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
        },
    },
}

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

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. Вот пример, который отключает конфигурацию логгирования 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) [source]

Этот обработчик отправляет электронное письмо администраторам сайта 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',
    }
},

По умолчанию используется экземпляр почтового backend, указанный в EMAIL_BACKEND.

send_mail(subject, message, *args, **kwargs) [source]

Отправляет электронные письма администраторам. Чтобы настроить это поведение, вы можете создать подкласс класса AdminEmailHandler и переопределить этот метод.

Фильтры

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

class CallbackFilter(callback) [source]

Этот фильтр принимает функцию обратного вызова (которая должна принимать один аргумент, запись, которая должна быть записана в журнал), и вызывает её для каждой записи, прошедшей через фильтр. Обработка этой записи не будет продолжена, если функция обратного вызова вернёт 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 [source]

Этот фильтр пропускает записи только тогда, когда настройка 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 [source]

Этот фильтр похож на 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/2.2/topics/logging/

Spec-Zone.ru

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