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

Настройка журнала Python для библиотеки / приложения

Привет [Dev] мир! Это мой первый пост здесь 🙂 Я начал использовать библиотеку регистрации Python A … Помечено Python, Logging, приложение, HOWTO.

Привет [Dev] мир! Это мой первый пост здесь:-)

Я начал использовать Библиотека регистрации Python Несколько лет назад; Это оконктно мощное lib, и я призываю вас принять его в ваших событиях кода.

Есть бесчисленные веб-страницы с Как И Учебники, объясняющие, как установить систему ведения журнала для вашего файлового сценария Python. Тем не менее, трудно найти места, которые объясняют, как настроить библиотеку журнала Python, которая должна использоваться наложение, и как правильно интегрировать и поделиться Регистрация Во всех ваших прикладных модулях удобно. Вы все еще можете найти несколько примеров в Интернете и в Официальная документация поваренная книга . Изложенное отсутствие учебных пособий особенно верно, если ваш проект фокусируется как на разработке библиотеки, так и для распределения интерфейса не-разработчика. В этих случаях необходимо настроить журнал так, что он обеспечивает правильный выход, когда пользователи используют ваше приложение в качестве библиотеки, и когда пользователи используют его как автономное программное обеспечение. В последнем случае конечные пользователи ожидают .бревно и/или .debug файлы с записью выполнения. С другой стороны, пользователи ваших библиотек не понадобятся такие файлы, и почти наверняка напечатано сообщение sys.stdout или Sys.Stderr хватает. Итак, вопрос остается на том, как правильно настроить такие разностные интерфейсы. Вы увидите, что когда-то настроили, настройка выглядит довольно простой и очевидной. Но, честно говоря, потребовалось во мне время разложить Регистрация Функциональные возможности до минимального набора операций, соответствующих этим требованиям. Для этой конфигурации журнала мы использовали два основных концепция: 1) глобальные переменные и 2) тот факт, что мы можем настроить Регистрация динамично. Я представлю решение, которое я в настоящее время использую. Пожалуйста, поделитесь своими мыслями, если у вас есть другой или лучший подход.

Настройка регистратора широко

Рассмотрим следующую структуру проекта:

SampleProject/ # <- main repository folder
    src/
        sampleproject/
            __init__.py
            base.py (here is where your main API interface goes)
            logger.py
            package1/
                __init__.py
                pkg1_module.py
            (... etc ...)
    docs/
    (... other files...)

Что мы хотим это настроить наши Логин объект до любой из модулей проекта инициирует так, чтобы они могли использовать бревно прямо с самого начала.

Для этого, прямо в образецпроект root __init__.py. мы определяем главную Логин с уровнем ведения журнала, установленным на Отладка Отказ В конфигурации я представляю здесь, что я использую в наши дни, я определяю Один Логин Это служит всему приложению/библиотеке/пакету.

# in src/sampleproject/__init__.py
import logging

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

Журнал Сейчас создается и является глобальной переменной, установленной на главном проекте __init__.py файл.

Во-вторых, а сразу после в __init__.py. Я определяю лесозаготовительный обработчик, ответственный за запись, по умолчанию, к Sys.Stderr Подробнее о StreamHandler Отказ В элементарных словах это обработчик регистратора, который заменяет Печать функция. Я не объясняю лесозаготовительные обработчики Здесь, для этого, как сказано вначале, там проходят бесчисленные высококачественные объяснения:

# in src/sampleproject/__init__.py
import logging

from sampleproject.logger import INFOFORMATTER


log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

# defines the stream handler
_ch = logging.StreamHandler()  # creates the handler
_ch.setLevel(logging.INFO)  # sets the handler info
_ch.setFormatter(logging.Formatter(INFOFORMATTER))  # sets the handler formatting

# adds the handler to the global variable: log
log.addHandler(_ch)

Вы замечаете, что переменная Infoformatter ? Я импортирую его из logger.py модуль в образецпроект Когда я определяю дополнительные переменные или функции помощи или функции, связанные с журналом, например, форматтерами:

# in src/sampleproject/logger.py
DEBUGFORMATTER = '%(filename)s:%(name)s:%(funcName)s:%(lineno)d: %(message)s'
"""Debug file formatter."""

INFOFORMATTER = '%(message)s'
"""Log file and stream output formatter."""

Как правило, я определяю высокую функцию Delbose Debug, которая идентифицирует файл и строку, где сообщение зарегистрировано и, с другой стороны, я использую простое и удобное для пользователя для Информация Уровень журнала, который является только сообщением, которую необходимо распечатать программа.

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

Например:

# in src/sampleproject/package1/pkg1_module1.py
from sampleproject import log

# and then just use it
log.info('message')

Вы можете использовать его одинаково, есть ли он внутри классов или внутри функций.

def myfunct(args):
    # do something
    log.info('worked, this is the result {}'.format(result))
    return result

Поместите так, это звучит без усилий.

Другие соображения в файле __init__

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

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

__init__.py Макет, наконец, выглядит так:

"""
PROJECT MAIN DOCSTRING
"""
# import ... # Python standard library imports here
import logging

from sampleproject.logger import (
    DEBUGFILE,
    DEBUGFORMATTER,
    INFOFILE,
    INFOFORMATTER,
    )

# this is exactly what we explained before
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
_ch = logging.StreamHandler()
_ch.setLevel(logging.INFO)
_ch.setFormatter(logging.Formatter(INFOFORMATTER))
log.addHandler(_ch)

# finally you would import from your project what you need
# to bring to the root NameSpace, if you need to do so.
from sampleproject.package1.module1 import SuperClass, megafunction

Это перерывы, в моем понимании, сопоставляемое правило Pep8, потому что мы импортируем только после настроенного журнала. Если вы используете Checker Lint, например flake8. и isort. Возможно, вам может потребоваться добавить оператор игнорирования, в конце строки импорта:

from sampleproject.package1.module1 import SuperClass, megafunction  # noqa: F401 isort:skip

Это круговой импорт?

Хотя Superclass и Мегафункция Используйте Журнал Определяется в главном __init___.py Файл и этот импортирует бывшие два объекта, я не нашел проблем с циркулярным импортом. Вы находите его в противном случае?

Настройка файлов регистрации для пользователей

Как сказано, что если вы разрабатываете проект, который обслуживает как в библиотеке, так и в автономном приложении, вам необходимо создать файлы журнала для выполнения программ. Я обычно создаю как минимум два файла: один info.log и один Debug.log Отказ Как и ожидалось, info.log Регистрирует все сообщения с Информация Уровень во время Debug.log Регистрирует все Отладка Минимальные сообщения уровня. Как мы можем отделить создание файлов журналов из самого объекта журнала, так что первая работа работает только тогда, когда программа работает как автономное программное обеспечение, например, в приложении командной строки? Проще говоря, добавьте обработчики которые настраивают оба журнала в исполнении Точка входа Отказ Как?

В logger.py Модуль, определить init_log_files Функция, которая называется из Точка входа Когда образецпроект выполняется как автономная программа. Если образецпроект Используется как библиотека, эти файлы не будут созданы.

# in src/sampleproject/logger.py
def init_log_files(log, mode='w'):
    """
    Initiate log files.

    Two files are initiated:

    1. :py:attr:`myapp.logger.DEBUGFILE`
    2. :py:attr:`myapp.logger.INFOFILE`

    Adds the two files as log Handlers to :py:attr:`log`.

    Parameters
    ----------
    mode : str, (``'w'``, ``'a'``)
        The writing mode to the log files.
        Defaults to ``'w'``, overwrites previous files.    """
    # here, I show a very simple configuration
    # but you can extend it to how many handlers
    # and tweaks you need.
    db = logging.FileHandler(DEBUGFILE, mode=mode)
    db.setLevel(logging.DEBUG)
    db.setFormatter(logging.Formatter(DEBUGFORMATTER))

    info = logging.FileHandler(INFOFILE, mode=mode)
    info.setLevel(logging.INFO)
    info.setFormatter(logging.Formatter(INFOFORMATTER))

    log.addHandler(db)
    log.addHandler(info)

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

# in a CLI file
# for example, src/sampleproject/cli_1.py
from sampleproject import log
from sampleproject.logger import init_log_files

# some where down the line
def main(*args, **kwargs):
    init_log_files(log)
    # continue operating

Любые дополнительные конфигурации, такие как выделенные CLI Журнальные имена файлов, могут быть добавлены в качестве параметров в init_log_files Отказ

Резюме

В этом посте я поделился с тобой:

  • В настоящее время я настрою Python Регистрация Библиотека для обслуживания проекта в целом, с несколькими пакетами и модулями
  • Чтобы отделить настройку ведения журнала и создание обработчиков файлов, это позволяет журналу служить как для библиотечных целей, так и для автономных приложений
  • Активируйте обработчики файлов журнала на CLI запрос

Я надеюсь, что вы найдете эту статью интерес, и я с нетерпением жду ваших комментариев.

Примечание: Этот пост также доступен в мой сайт Отказ

Оригинал: “https://dev.to/joaomcteixeira/setting-up-python-logging-for-a-library-app-6ml”