Регистрация – один из лучших способов отслеживания того, что происходит внутри вашего кода, когда он работает. Сообщение об ошибке может сообщить вам подробности о состоянии приложения, когда произойдет ошибка. Но правильное ведение журнала облегчит пропустить состояние приложения прямо до того, как произошло ошибка, либо путь, которые сделали данные, прежде чем ошибка произошла.
Что в этой статье?
- Основной обзор системы регистрации Python.
- Объяснение того, как каждый модуль для ведения журнала взаимодействует друг с другом.
- Примеры о том, как использовать различные типы конфигураций регистрации.
- Вещи, чтобы посмотреть на
- Рекомендации для лучшего ведения журнала
Если вы хотите, вы можете перейти в основной пример конфигурации или полноценный пример, используемый в приложении.
Основы
У самой простой формы журнальное сообщение в Python называется Logrecord
имеет строку (сообщение) и A Уровень Отказ Уровень может быть:
Критический
: Любая ошибка, которая заставляет приложение перестать работать.Ошибка
.: Общие сообщения об ошибках.Предупреждение
: Общие предупреждающие сообщения.Информация
: Общие информационные сообщения.Отладка
Любые сообщения, необходимые для отладки.Не значительно
: Уровень по умолчанию используется в обработчиках для обработки любых из вышеуказанных типов сообщений.
Библиотека регистрации Python имеет разные модули. Не каждый модуль не нужно для начала регистрации сообщений.
- Легенераторы: Объект, который реализует основное ведение журнала API. Мы используем регистратор для создания событий ведения журнала, используя соответствующий уровень уровня, например,
my_logger.debug ("Мое сообщение")
Отказ По умолчанию есть корневой регистратор, который можно настроить с помощью PASICCONFIG И мы также можем создать Пользовательские регистраторы Отказ - Форматтеры: Эти объекты отвечают за создание правильного представления события ведения журнала. Большую часть времени мы муравьем создаем читаемое человеком представление, но в некоторых случаях мы можем выводить определенный формат, такой как объект JSON.
- Фильтры: Мы можем использовать Фильтры Чтобы избежать событий для регистрации печати на основе более сложных критериев, чем уровень регистрации. Мы также можем использовать фильтры для изменения событий регистрации.
- Обработчики: Все собирается вместе в обработчики Отказ Обработчик определяет, какие формы и фильтруют регистратор, который будет использовать. Обработчик также отвечает за отправку вывода регистрации в соответствующее место, это может быть
stdout.
,Email
Или почти все, что еще мы можем думать.
Логисты могут иметь фильтры и обработчики. Художественные обработчики могут иметь фильтры и форматтеры. Мы погрузимся в каждую из этих частей, но это хорошая идея, чтобы иметь в виду, как они связаны друг с другом. Вот визуальное представление:
Структура регистратора Python
Следующие философии Python Buckging Library можно легко использовать, например:
import logging logging.warning("Warning log message.")
Вот и все. Пример выше будет напечатан сообщение журнала с Предупреждение
уровень. Если вы запустите код выше, вы увидите этот вывод:
WARNING:root:Warning log message.
Первая часть вывода ( Предупреждение: root:
), исходит от Конфигурация журнала по умолчанию. Конфигурация по умолчанию будет только печатать Предупреждение
и выше уровня и будет представлять уровень журнала и имя регистратора – Так как мы не создали какие-либо регистраторы root
регистратор используется. Больше на это позже -.
Регистратор в примере выше является root
логин. Это регистратор, который является родителем каждого созданного регистратора. Значение, если вы настраиваете root
Регистратор, то вы в основном настроили каждый регистрацию регистратора.
Что такое ParasonConfig?
Чтобы быстро настроить любые регистраторы, используемые в вашем приложении, вы можете использовать BasicConfig
Отказ Этот метод будет по умолчанию настроить корневой регистратор с помощью StreamHandler
Печать сообщения журнала на stdout.
и форма по умолчанию (например, тот, который в примере выше).
Мы можем настроить формат ведения журнала Использование BasicConfig
Отказ Например, если бы мы хотели бы распечатать только уровень журнала, номер строки, журнал и журнал Отладка
События и вверх мы можем сделать это:
import logging logging.basicConfig( format="[%(levelname)s]:%(lineno)s - %(message)s", level=logging.DEBUG )
⚡ Примечание: Используя регистрация .basicconfig.
Мы настраиваем корневой регистратор
Для каждого события журнала есть экземпляр Logrecord
Отказ Мы можем установить формат наших сообщений журнала, используя Logrecord
классные атрибуты и %
Форматирование-сцепление – %
Форматирование-сцепление все еще используется для поддержания обратной совместимости -. Поскольку Python 3.2 Мы также можем использовать $
и {}
Стиль для форматирования сообщений, Но мы должны указать стиль, который мы используем, по умолчанию %
-пин используется. Стиль настроен с помощью Стиль
Параметр в BasicConfig
( logging.basicconfig (стиль = '{')
). При обновленной конфигурации мы теперь можем увидеть каждое сообщение уровня журнала, например:
[CRITICAL]:9 - Critical log message. [ERROR]:10 - Error log message. [WARNING]:11 - Warning log message. [INFO]:12 - Info log message. [DEBUG]:13 - Debug log message.
🐍 Лучшая практика Определите пользовательский формат журнала для облегчения анализа журнала.
⚡ Примечание: Используйте Logrecord
Класс «Атрибуты Чтобы настроить пользовательский формат.
BasicConfig
Всегда будет вызываться корневым регистратором, если не определены обработчики, если только параметр сила
установлен на Истинный
( logging.basicconfig (force = true)
). По умолчанию BasicConfig
будет настроить корневой регистратор в выходные журналы для stdout
с форматом по умолчанию {уровень}: {logger_name}: {message}
Отказ
Нестандартные регистраторы
Библиотека регистрации Python позволяет нам создавать пользовательские регистраторы, которые будут всегда быть детьми root
логин.
Мы можем создать новый регистратор, используя LOGGALE.GETLOGGER («MYLOGGER»)
Отказ Этот метод принимает только один параметр, Имя
регистратора. GetLogger
это get_or_create метод. Мы можем использовать GetLogger
с тем же имя
стоимость И мы будем работать с той же конфигурацией регистратора, независимо от того, делаем это в другом классе или модуле.
Библиотека регистрации использует точечную обозначение на Создать иерархии регистраторов. Значение, если мы создадим три регистратора с именами приложение
, app.models
и app.api
Родительский регистратор будет приложение
и, app.db
и app.api
Будут детьми. Вот визуальное представление:
+ app # main logger | + app.api # api logger, child of "app" logger | - app.api.routes # routes logger, child of "app" and "app.api" loggers | - app.api.models # models logger, sibling of "app.api.routes" logger | - app.utils # utils logger, sibling of "app.api" logger
Мы можем получить имя нотации модуля из глобальной переменной __name__
Отказ Использование __name__
Для создания наших пользовательских регистраторов упрощает конфигурацию и избежать столкновений:
# project/api/utils.py import logging # Use __name__ to create module level loggers LOG = logging.getLogger(__name__) def check_if_true(var): """Check if variable is `True`. :param bool var: Variable to check. :return bool: True if `var` is truthy. """ LOG.info("This logger's name is 'project.api.utils'.") return bool(var)
⚡ Примечание: Если вы определяете регистраторы на уровне модуля (например, пример выше) лучше придерживаться глобального переменного имена. Значение, использовать Журнал
или Логин
вместо строевой версии журнал
или Логин
Отказ
🐍 Лучшая практика Создание регистраторов с пользовательскими именами, использующими __name__
Чтобы избежать столкновения и для гранулированная конфигурация.
При использовании регистратора уровня сообщения проверяется против Mogger’s Уровень
атрибут, если оно то же самое или выше, то сообщение журнала передается в систему, который используется и каждый родитель Разве Один из логиза в иерархии наборы пропагандировать
к Ложь
– по умолчанию пропагандировать
установлен на Истинный
–.
Как настроить регистраторы, форматтеры, фильтры и обработчики
Мы говорили об использовании BasicConfig
Чтобы настроить регистратор Но есть Другие способы настроить регистраторы. Рекомендуемый способ создания конфигурации журнала использует A Dictconfig
Отказ Примеры в этой статье будут использоваться Dictconfig
Отказ
Как мы узнали до регистрации Python Библиотека уже поставляется с некоторыми полезными значениями по умолчанию, что делает определение наших собственных форммеров, фильтров и обработчиков. На самом деле, при использовании словаря, чтобы настроить регистрацию только требуемого ключа Версия
и в настоящее время единственное допустимое значение – 1
Отказ
Всякий раз, когда мы используем регистратор ( log.debug (сообщение о отладке журнала ».)
) Первое, что происходит, это то, что Logrecord
Объект создан с нашим журналом и другим атрибуты . Это Logrecord
экземпляр затем передан на любой Фильтры Прикреплен к использованию экземпляра регистратора. Если фильтр не отвергает Logrecord
экземпляр тогда Logrecord
передается на настроенный обработчики . Если какой-либо из настроеншей обработчики включены для уровня в прошлому Logrecord
Тогда обработчики Применить любой настроенный фильтры к Logrecord
. Наконец, если Logrecord
не отклоняется ни одним из Фильтр Logrecord
испускается. Более подробную диаграмму можно увидеть в Документация Python Отказ
Чтобы упростить потоку журнала Python, мы можем сосредоточиться на том, что происходит в одном регистратере:
Поток журнала Python упрощенный
Вот несколько важных вещей, которые следует отметить:
- Диаграмма выше, с точки зрения используемого журнала.
- Фильтры, обработчики и форматтеры определены один раз и могут использоваться несколько раз.
- Только Фильтры и форматтеры, назначенные для обработчика родителей, применяются к
Logrecord
(Это цикл, который говорит «родительские регистраторы *»)
Каждое созданное событие журнала будет обработано, за исключением следующих случаев:
- Регистратор или обработчик, сконфигурированный в регистратере, не включен для используемого уровня журнала.
- Фильтр, настроенный в регистратере или обработчике, отклоняет событие журнала.
- Детский регистратор имеет
пропагандировать = Ложь
вызывая события, которые не будут переданы ни одному из родительских регионов. - Вы используете другой регистратор или регистратор не является родителем, используемого.
Форматтеры
А Формировал объект превращает Logrecord
экземпляр в читабельную строку человека или строку, которая будет потребляться внешней службой. Мы можем использовать любой Logrecord
атрибут Или что-нибудь отправлено в вызов в лесозаготовке как Дополнительно
параметр.
⚡ Примечание: Форматтеры могут быть установлены только на обработчики
Например, мы можем создать Fixter, чтобы показать все детали того, где произошло сообщение журнала:
LOGGING_CONFIG = { "version": 1, "formatters": { "detailed": { "format": "[APP] %(levelname)s %(asctime)s %(module)s " "%(name)s.%(funcName)s:%(lineno)s: %(message)s" } }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "detailed", "level": "INFO" } } }
При использовании словаря для определения форматирования мы должны использовать "Форматтеры"
ключ. Любой ключ внутри "Форматтеры"
Объект станет форматированным. Каждое клавише/значение внутри объекта Fixter будет отправлено в качестве параметров при включении экземпляра Fixatter.
Всякий раз, когда этот форматер используется, он будет распечатать уровень, дату и время, имя модуля, имя функции, номер строки и любую строку, отправленную в качестве параметра. Мы можем добавить другие переменные, не присутствующие в Logrecord
атрибуты с помощью Дополнительно
атрибут:
LOGGING_CONFIG = { "version": 1, "formatters": { "short": { "format": "[APP] %(levelname)s %(asctime)s %(module)s " "%(name)s.%(funcName)s:%(lineno)s: " "[%(session_key)s:%(user_id)s] %(message)s" # add extra data } }, #[...] }
И отправлять эти дополнительные данные, которые мы можем сделать это так:
# app.api.UserManager.list_users LOG.info( "Listing users", extra={"session_key": session_key, "user_id": user_id} )
Вывод будет:
[APP] INFO 2020-11-07 20:47:00,123 user_manager app.api.user_name.list_users:15 [123abcd:api_usr] Listing users
Недостатком переменных ссылок, отправленные через Дополнительно
Параметр в формате заключается в том, что если переменная не будет передана событие журнала не будет зарегистрировано, потому что строка не может быть создана.
⚡ Лучшая практика Обязательно отправьте каждую дополнительную переменную что настроен Форматированные ссылки на использование Дополнительно
параметр
Фильтры
Фильтры очень интересные. Они могут использоваться как на уровне регистратора, либо на уровне обработчика, их можно использовать для остановки определенных событий журнала от регистрации, и они также могут быть использованы для введения дополнительного контекста в Logrecord
Экземпляр, который будет, в конечном итоге, зарегистрирован.
Фильтр
Класс в Python’s Регистрация
Фильтры библиотеки Logrecords
по имени регистратора. Фильтр позволит любому Logrecord
Исходя из имени регистратора, настроенного в фильтре и любой из его детей.
Например, если у нас есть эти логики настроили:
+ app.models | | | - app.models.users | + app.views | - app.views.users | - app.views.products
И мы определяем конфигурацию фильтра, как это:
LOGGING_CONFIG = { "version": 1, "filter": { "views": { "name": "app.views" } }, "handlers": { "console": { "class": "logging.StreamHandler", "level": "INFO", "filter": "views" } }, "loggers": { "app": { "handlers": ["console"] } } }
Мы используем «Фильтр»
Ключ в конфигурации словаря, чтобы определить любые фильтры. Каждый ключ внутри «Фильтр»
Объект станет фильтром, который мы можем позже ссылаться. Каждый ключ и значение внутри объекта фильтра, который мы создаем, будет отправлено в качестве параметров при наличии фильтра.
Предыдущая конфигурация будет Только Разрешить Logrecord
исходя из app.views
, app.views.Users
и app.views.products
Легенгеры.
Когда вы устанавливаете фильтр на определенный регистратор, фильтр будет Только использоваться при вызове этого регистратора напрямую и не Когда используется потомка указанного регистратора. Например, если мы установили фильтр в предыдущий пример к app.view
регистратор вместо приложение
Обработчик регистратора. Фильтр не будет отклонить любой Logrecord
исходя из app.models
регистраторы просто потому, что при использовании регистратора app.models
или любое из этого детей, фильтр не будет вызван.
Посмотрим, как мы можем создать пользовательские фильтры для предотвращения Logrecords
быть обработанным на основе более сложных условий и/или добавить больше данных на Logrecord
Отказ
С момента Python 3.2 Вы можете использовать любой Callable, который принимает Запись
параметр
def custom_filter(record): """Filter out records that passes a specific key argument""" # We can access the log event's argument via record.args return record.args.get("instrumentation") == "console"
Способ настроить эти пользовательские классы/озвонки с использованием словаря или файла, используя специальное ключевое слово ()
. Всякий раз, когда конфигурация журнала Python видит ()
Это создаст экземпляр класса (необходимо использовать точечную обозначение).
⚡ Примечание: При использовании словаря для настройки журнала вы можете использовать ()
Ключевое слово для настройки пользовательских обработчиков или фильтров
Еще одна интересная вещь о фильтрах состоит в том, что они видят практически каждый Logrecord
что может быть зарегистрированным. Это делает фильтрами отличным местом для дальнейшего настроения Logrecords.
. Это называется «добавление контекста».
Пример пользовательского фильтра
Следующий пользовательский фильтр применит маску для каждого пароля, передаваемого на Logrecord
Если парусник
или пароль
ключ используется.
# module: app.logging.filters def pwd_mask_filter(record) """A factory function that will return a filter callable to use.""" def filter(record): # a Logger cord instance holds all it's arguments in record.args def mask_pwd(record): return '*' * 20 if record.args.has_key("pwd"): record.args["pwd"] = mask_pwd() elif record.args.has_key("password"): record.args["pwd"] = mask_pwd() return filter
LOGGING_CONFIG = { "version": 1, "formatters": { "detailed": { "format": "[APP] %(levelname)s %(asctime)s %(module)s " "%(name)s.%(funcName)s:%(lineno)s: %(message)s" } }, "filters": { "mask_pwd": { "()": "app.logging.filters.mask_pwd" } }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "detailed", "level": "INFO", "filters: ["mask_pwd"] } } }
При использовании ()
В конфигурации словаря ссылок модуль будет импортирован и создан. В этом случае функция завода mask_pwd
Будут вызваться и фактическая функция, которая обрабатывает фильтрацию, будет возвращена.
Обработчики
Обработчики – это объекты, которые реализуют, как используются форматтеры и фильтры. Если мы помним схему потока, мы можем видеть, что всякий раз, когда мы используем регистратор, обработчики всех родительских журналов будут называться Recursivley. Именно здесь вся картина состоит вместе, и мы можем взглянуть на полную конфигурацию вход в логину, используя все остальное, о котором мы говорили.
Python предлагает список очень полезных обработчики Отказ Классы обработчика имеют очень простое API и в большинстве Шкафы мы будем использовать эти классы только для добавления файлов Fixter, ноль или более и установку уровня регистрации.
Примечание: Только один Formatter можно установить в обработчик.
Например, следующая конфигурация гарантирует, что каждый журнал печатается на стандартном выходе, использует фильтр для выбора другого обработчика в зависимости от того, работает ли приложение в режиме отладки или нет, и он использует другой формат для каждой другой среды (на основе флаг отладки).
Полный пример конфигурации
LOGGING = { "version": 1, "disable_existing_loggers": False, "formatters": { "long": { "format": "[APP] {levelname} {asctime} {module} " "{name}.{funcName}:{lineno:d}: {message}", "style": "{" }, "short": { "format": "[APP] {levelname} [{asctime}] {message}", "style": "{" } }, "filters": { "debug_true": { "()": "filters.require_debug_true_filter" }, "debug_false": { "()": "filters.require_debug_false_filter" } }, "handlers": { "console": { "level": "INFO", "class": "logging.StreamHandler", "formatter": "short", "filters": ["debug_false"] }, "console_debug": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "long", "filters": ["debug_true"] } }, "loggers": { "external_library": { "handlers": ["console"], "level": "INFO" }, "app": { "handlers": ["console", "console_debug"], "level": "DEBUG" } } }
При использовании конфигурации словаря disable_existing_loggers
установлен на Истинный
по умолчанию. Что это делает, так это то, что он будет захватить все регистраторы, созданные до применения конфигурации, и отключайте их, поэтому они не могут обработать какие-либо события журнала. Это особенно важно при создании регистраторов на уровне модуля.
Скажем, у нас есть app.models.client
Модуль, в котором мы создаем вроде регистратора:
# app.models.client import logging LOG = logging.getLogger(__name__) class Client: # ...
И затем в точке входа нашего приложения мы импортируем наши модели и настроем нашу ведение журнала:
# app.__main__ import logging from app.settings.logging import LOGGING_CONFIG # A dictionary from app.models import Client logging.config.dictConfig(LOGGING_CONFIG)
В этом случае регистратор создан в app.models.client
не будет работать, если disable_existing_loggers
Отказ Потому что когда мы импортируем Клиент
класс от app.models.
Мы инициализируем, используемое в app.models.client
Отказ Затем после того, как регистратор уже инициализирован, мы применяем нашу конфигурацию, что делать какие-либо существующие регистраторы.
Когда определенный обработчик, используя словарь, любой ключ, который не является класс
, Уровень
, Formatter
или Фильтры
будет передан как параметр к классу обработчика, указанному для мрак.
Фильтры не могут использоваться при использовании файла для настройки журнала. Еще одна причина, почему лучше использовать словарь для конфигурации.
Вещи для поиска при настройке ваших логиков
- Фильтры могут быть установлены как в Loggers, так и обработчики. Если фильтр установлен на уровне регистратора, он будет использоваться только тогда, когда используется только конкретный регистратор, а не при использовании ни одного из его потомков.
- Конфигурация файла все еще поддерживается исключительно для обратной совместимости. Избегайте использования файла CONFIG.
()
Может использоваться в словарях конфигурации для указания пользовательского класса/Callable для использования в качестве форматирования, фильтра или обработчика. Предполагается, что завод ссылается при использовании()
Чтобы обеспечить полное управление инициализацией.- Перейти через Список случаев Когда событие журнала не обрабатывается, если вы не видите то, что вы ожидаете в ваших журналах.
- Обязательно установите
disable_existing_loggers
кЛожь
При использовании Dict Config и создание регистраторов на уровне модуля.
Рекомендации для лучшего ведения журнала
- Найдите некоторое время, чтобы спланировать, как должны выглядеть ваши журналы, и где они будут храниться.
- Убедитесь, что ваши журналы легко разрабатываются как визуально, так и программно, как использовать
Греп
Отказ - Большую часть времени вам не нужно устанавливать фильтр на уровне регистратора.
- Фильтры могут быть использованы для добавления дополнительного контекста для журнала событий. Убедитесь, что любые пользовательские фильтры быстро. Если ваши фильтры проводят внешние вызовы, чтобы использовать Обработчик очереди
- Журнал больше, чем ошибки. Убедитесь, что когда вы видите ошибку в журналах, которые вы можете отследить состояние данных в журналах.
- Если вы разрабатываете библиотеку, всегда устанавливайте
Нулендлер
обработчик И пусть пользователь определит конфигурацию журнала. - Используйте обработчики, уже предлагаемые библиотекой журнала Python, если не очень особенный случай. Большую часть времени вы можете отдать журналы, а затем обработать их с более специализированным инструментом. Например, отправить журналы в
Syslog
ИспользованиеSysloghandler
обработчик а затем использоватьRsyslog
поддерживать ваши журналы. - Создайте новый регистратор, используя имя модуля, чтобы избежать столкновений,
loggating.getLogger (__ имя__)
⚡ Примечание: Если вы хотите использовать тест конфигурации выше или просто воспроизводить с разными конфигами, Оформить заказ Дополнительный репо к этой статье
Особая благодарность Žan anderle для доказательства чтения.
Следуй за мной на Twitter @RMComucity
Оригинал: “https://dev.to/rmcomplexity/introduction-to-python-logging-4p3o”