Рубрики
Без рубрики

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

Регистрация – один из лучших способов отслеживать, что происходит внутри вашего кода, пока он работает … Теги с Python, основы, регистрация.

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

Что в этой статье?

  • Основной обзор системы регистрации 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 (Это цикл, который говорит «родительские регистраторы *»)
Каждое созданное событие журнала будет обработано, за исключением следующих случаев:
  1. Регистратор или обработчик, сконфигурированный в регистратере, не включен для используемого уровня журнала.
  2. Фильтр, настроенный в регистратере или обработчике, отклоняет событие журнала.
  3. Детский регистратор имеет пропагандировать = Ложь вызывая события, которые не будут переданы ни одному из родительских регионов.
  4. Вы используете другой регистратор или регистратор не является родителем, используемого.

Форматтеры

А Формировал объект превращает 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”