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

Краткое введение в ведение журнала

Django использует logging встроенный модуль Python для ведения системного журнала. Использование этого модуля подробно обсуждается в собственной документации 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 (что используется по умолчанию, dictConfig если ключ отсутствует), все средства ведения журнала в конфигурации по умолчанию будут отключены. Отключение регистратора не эквивалентно его удалению; регистратор все еще существует, но он молча отбрасывает все полученные сообщения, даже не передавая их родительскому регистратору. Поэтому вам следует с осторожностью относиться к настройке ; он часто делает не то, что нам хотелось бы. Тем не менее, вы можете установить , чтобы и переопределить некоторые или все регистраторы по умолчанию. Другая стратегия заключается в создании в и сделать конфигурацию протоколирования самостоятельно .'disable_existing_loggers': True disable_existing_loggers False LOGGING_CONFIG None

Ведение журнала настраивается как часть setup() общей функции Django. Таким образом, вы можете быть уверены, что регистраторы всегда готовы к использованию кодом вашего проекта.

Примеры

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

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

  • Он определяет два фильтра:

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

    • console , обработчик для StreamHandler вывода сообщения INFO об ошибке любого уровня или выше ( sys.stderr ). Этот менеджер использует формат отображения simple .
    • mail_admins , менеджер, AdminEmailHandler который уведомляет ADMINS сайт по электронной почте о любом уровне сообщения ERROR или выше. Этот менеджер использует фильтр отображения 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. По умолчанию это указывает на функцию logging.config.dictConfig() Python. Однако, если вы хотите использовать другой метод конфигурации, вы можете указать любой другой исполняемый файл, который принимает один параметр. Содержимое 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.*

Регистраторы 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.db.backends.schema

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

Менеджеры

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

classAdminEmailHandler ( include_html = False , email_backend = None , reporter_class = None )

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

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

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

Параметр include_html in 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( тема , сообщение , * аргументы , ** kwargs )

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

Фильтры

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

классCallbackFilter ( обратный вызов )

Этот фильтр принимает функцию обратного вызова (которая должна принимать только один параметр - запись, которая должна регистрироваться) и вызывает ее для каждой записи, проходящей через этот фильтр. Обработка записи останавливается, если функция обратного вызова возвращается 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'
    }
},
класс 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'
    }
},
класс 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, передают ведение журнала своим родителям, вплоть до корневого регистратораdjango . Обработчики console и mail_admins связаны с корневым регистратором для обеспечения описанного выше поведения.

Смотрите также Logging Конфигурация для того, как дополнить или заменить эту конфигурацию регистрации по умолчанию, определенный в Джанго / Utils / log.py .

Copyright ©2020 All rights reserved